sblunt / orbitize

Orbit-fitting for directly imaged objects
https://orbitize.info
Other
74 stars 43 forks source link

Need to protect entry point when running the MCMC sampler outside of a notebook #359

Closed bailer-jones closed 6 months ago

bailer-jones commented 7 months ago

I am using the MCMC sampler for fitting the orbit of a single planet. If I run my code directly in the terminal, I get the following error:

RuntimeError:
        An attempt has been made to start a new process before the
        current process has finished its bootstrapping phase.

        This probably means that you are not using fork to start your
        child processes and you have forgotten to use the proper idiom
        in the main module:

            if __name__ == '__main__':
                freeze_support()
                ...

        The "freeze_support()" line can be omitted if the program
        is not going to be frozen to produce an executable.

The code then seems to run forever. I do not get this error if I run the same code in a Jupyter notebook (i.e. interactively).

I am running orbitize 2.2.2 in python 3.9.18 on MacOs Sonoma 14.3 An outline of my set up is as follows:

    sys = system.System(num_secondary_bodies=1, use_rebound=False, fit_secondary_mass=False,
                        data_table=passdata, tau_ref_epoch=timeref, 
                        stellar_or_system_mass=mass_star/msun, mass_err=0, 
                        plx=1.0, plx_err=0.0)
    lab = sys.param_idx # alias
    sys.sys_priors[lab['sma1']] = priors.GaussianPrior(sma_b/au, sma_sd_b/au, no_negatives=True) # [au]
    sys.sys_priors[lab['ecc1']] = priors.GaussianPrior(ecc_b, ecc_sd_b, no_negatives=True) # [rad]
    sys.sys_priors[lab['inc1']] = priors.GaussianPrior(inc_b, inc_sd_b, no_negatives=False) # [rad]
    sys.sys_priors[lab['aop1']] = priors.GaussianPrior(omega_b, omega_sd_b, no_negatives=False) # [rad]
    sys.sys_priors[lab['pan1']] = priors.GaussianPrior(Omega_b, Omega_sd_b, no_negatives=False) # [rad]
    sys.sys_priors[lab['tau1']] = priors.GaussianPrior(T_frac_b, T_frac_sd_b, no_negatives=True) # [rad]
    mySampler = sampler.MCMC(sys, num_temps=mcmcpars['num_temps'], 
                             num_walkers=mcmcpars['num_walkers'], num_threads=mcmcpars['num_threads'])
    blah = mySampler.run_sampler(total_orbits=mcmcpars['num_steps']*mcmcpars['num_walkers'], 
                                 burn_steps=mcmcpars['num_burnin'], thin=mcmcpars['thinfac'])

I think the problem originate MCMC run_sampler() using the python multiprocessing package.

I can avoid this problem if at the beginning of my program I add:

if __name__ == '__main__':
    # put all code here

At least, it doesn't then throw an error, and it appears to behave as expected.

sblunt commented 6 months ago

Hey @bailer-jones, thanks for the bug report! This is a quirk of Python multiprocessing that has to do with the Python global interpreter lock (GIL). It has to do with how Python initiates multiple parallel processes-- it creates a copy of the code, and each subprocess then runs using its own copy. Protecting the entry point (i.e. wrapping in if __name__ == '__main__' like you did) is necessary for making sure Python knows not to go on infinitely creating more subprocesses. There are multiple ways of actually creating the subprocesses (see here), but not all of them work across operating systems. You can try out the different methods-- they should all (I think) work in orbitize!, depending on your OS.

tl;dr-- this is orbitize! working as expected, and the error message thrown seems to have guided you toward the correct solution, so I don't think we'll change anything here.

Please reopen if my answer isn't clear and/or if you run into other related issues.