Skip to content

Commit

Permalink
.
Browse files Browse the repository at this point in the history
  • Loading branch information
petrelharp committed Jul 6, 2024
1 parent 8acb03b commit e537b27
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 93 deletions.
81 changes: 38 additions & 43 deletions stdpopsim/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@
pass


IS_WINDOWS = sys.platform.startswith("win")

logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -1041,47 +1039,44 @@ def time_or_model(
"This option may provided multiple times.",
)

# SLiM is not available for windows.
if not IS_WINDOWS:

def slim_exec(path):
# Hack to set the SLIM environment variable at parse time,
# before get_version() can be called.
os.environ["SLIM"] = path
return path

slim_parser = top_parser.add_argument_group("SLiM specific parameters")
slim_parser.add_argument(
"--slim-path",
metavar="PATH",
type=slim_exec,
default=None,
help="Full path to `slim' executable.",
)
slim_parser.add_argument(
"--slim-script",
action="store_true",
default=False,
help="Write script to stdout and exit without running SLiM.",
)
slim_parser.add_argument(
"--slim-scaling-factor",
metavar="Q",
default=1,
type=float,
help="Rescale model parameters by Q to speed up simulation. "
"See SLiM manual: `5.5 Rescaling population sizes to "
"improve simulation performance`. "
"[default=%(default)s].",
)
slim_parser.add_argument(
"--slim-burn-in",
metavar="X",
default=10,
type=float,
help="Length of the burn-in phase, in units of N generations "
"[default=%(default)s].",
)
def slim_exec(path):
# Hack to set the SLIM environment variable at parse time,
# before get_version() can be called.
os.environ["SLIM"] = path
return path

slim_parser = top_parser.add_argument_group("SLiM specific parameters")
slim_parser.add_argument(
"--slim-path",
metavar="PATH",
type=slim_exec,
default=None,
help="Full path to `slim' executable.",
)
slim_parser.add_argument(
"--slim-script",
action="store_true",
default=False,
help="Write script to stdout and exit without running SLiM.",
)
slim_parser.add_argument(
"--slim-scaling-factor",
metavar="Q",
default=1,
type=float,
help="Rescale model parameters by Q to speed up simulation. "
"See SLiM manual: `5.5 Rescaling population sizes to "
"improve simulation performance`. "
"[default=%(default)s].",
)
slim_parser.add_argument(
"--slim-burn-in",
metavar="X",
default=10,
type=float,
help="Length of the burn-in phase, in units of N generations "
"[default=%(default)s].",
)

subparsers = top_parser.add_subparsers(dest="subcommand")
subparsers.required = True
Expand Down
39 changes: 24 additions & 15 deletions stdpopsim/slim_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@

logger = logging.getLogger(__name__)


def _escape_eidos(s):
# this is for Windows paths passed as strings in Eidos
return "\\\\".join(s.split("\\"))


_slim_upper = """
initialize() {
if (!exists("dry_run"))
Expand Down Expand Up @@ -1171,7 +1177,7 @@ def fix_time(event):
recombination_rates=recomb_rates_str,
recombination_ends=recomb_ends_str,
generation_time=demographic_model.generation_time,
trees_file=trees_file,
trees_file=_escape_eidos(trees_file),
pop_names=f"c({pop_names_str})",
)
)
Expand Down Expand Up @@ -1489,7 +1495,7 @@ def matrix2str(
if logfile is not None:
printsc(
string.Template(_slim_logfile).substitute(
logfile=logfile,
logfile=_escape_eidos(str(logfile)),
loginterval=logfile_interval,
)
)
Expand Down Expand Up @@ -1631,21 +1637,27 @@ def simulate(

run_slim = not slim_script

mktemp = functools.partial(tempfile.NamedTemporaryFile, mode="w")
tempdir = tempfile.TemporaryDirectory(prefix="stdpopsim_")
ts_filename = os.path.join(tempdir.name, f"{os.urandom(3).hex()}.trees")

@contextlib.contextmanager
def script_file_f():
f = mktemp(suffix=".slim") if not slim_script else sys.stdout
yield f
if run_slim:
fname = os.path.join(tempdir.name, f"{os.urandom(3).hex()}.slim")
f = open(fname, "w")
else:
fname = "stdout"
f = sys.stdout
yield f, fname
# Don't close sys.stdout.
if not slim_script:
if run_slim:
f.close()

with script_file_f() as script_file, mktemp(suffix=".ts") as ts_file:

with script_file_f() as sf:
script_file, script_filename = sf
recap_epoch = slim_makescript(
script_file,
ts_file.name,
ts_filename,
demographic_model,
contig,
sample_sets,
Expand All @@ -1663,7 +1675,7 @@ def script_file_f():
return None

self._run_slim(
script_file.name,
script_filename,
slim_path=slim_path,
seed=seed,
dry_run=dry_run,
Expand All @@ -1673,7 +1685,7 @@ def script_file_f():
if dry_run:
return None

ts = tskit.load(ts_file.name)
ts = tskit.load(ts_filename)

ts = _add_dfes_to_metadata(ts, contig)
if _recap_and_rescale:
Expand Down Expand Up @@ -1713,7 +1725,6 @@ def _run_slim(
if slim_path is None:
slim_path = self.slim_path()

# SLiM v3.6 sends `stop()` output to stderr, which we rely upon.
self._assert_min_version("4.0", slim_path)

slim_cmd = [slim_path]
Expand Down Expand Up @@ -2002,6 +2013,4 @@ def recap_and_rescale(
return ts


# SLiM does not currently work on Windows.
if sys.platform != "win32":
stdpopsim.register_engine(_SLiMEngine())
stdpopsim.register_engine(_SLiMEngine())
35 changes: 0 additions & 35 deletions tests/test_slim_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,41 +56,6 @@ def test_bad_params(self):
dry_run=True,
)

def test_bad_samples(self):
engine = stdpopsim.get_engine("slim")
species = stdpopsim.get_species("HomSap")
contig = species.get_contig("chr1")
model = stdpopsim.PiecewiseConstantSize(species.population_size)
samples = [1, 2, ["foo"]]
with pytest.raises(ValueError, match="Samples must be a dict"):
engine.simulate(
demographic_model=model,
contig=contig,
samples=samples,
dry_run=True,
)
with pytest.raises(ValueError, match="Samples must be a dict"):
engine.recap_and_rescale(
ts=None,
demographic_model=model,
contig=contig,
samples=samples,
)
samples = [
msprime.SampleSet(
num_samples=2,
population=0,
ploidy=3,
)
]
with pytest.raises(ValueError, match="Sample ploidy other than 1 or 2"):
engine.simulate(
demographic_model=model,
contig=contig,
samples=samples,
dry_run=True,
)

@pytest.mark.filterwarnings("ignore::stdpopsim.SLiMScalingFactorWarning")
@pytest.mark.filterwarnings(
"ignore:.*model has mutation rate.*but this simulation used.*"
Expand Down

0 comments on commit e537b27

Please sign in to comment.