See CHANGELOG.md for a complete list of changes
pull/7/head 0.15.2
J. Fernando Sánchez 3 years ago
parent d6b684c1c1
commit e860bdb922

@ -3,6 +3,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.15.2]
### Fixed
* Pass the right known_modules and parameters to stats discovery in simulation
* The configuration file must exist when launching through the CLI. If it doesn't, an error will be logged
* Minor changes in the documentation of the CLI arguments
### Changed
* Stats are now exported by default
## [0.15.1]
### Added
* read-only `History`

@ -1 +1 @@
0.15.1
0.15.2

@ -23,13 +23,15 @@ def main():
import argparse
from . import simulation
logging.info('Running SOIL version: {}'.format(__version__))
logger.info('Running SOIL version: {}'.format(__version__))
parser = argparse.ArgumentParser(description='Run a SOIL simulation')
parser.add_argument('file', type=str,
nargs="?",
default='simulation.yml',
help='python module containing the simulation configuration.')
help='Configuration file for the simulation (e.g., YAML or JSON)')
parser.add_argument('--version', action='store_true',
help='Show version info and exit')
parser.add_argument('--module', '-m', type=str,
help='file containing the code of any custom agents.')
parser.add_argument('--dry-run', '--dry', action='store_true',
@ -52,12 +54,15 @@ def main():
args = parser.parse_args()
logging.basicConfig(level=getattr(logging, (args.level or 'INFO').upper()))
if args.version:
return
if os.getcwd() not in sys.path:
sys.path.append(os.getcwd())
if args.module:
importlib.import_module(args.module)
logging.info('Loading config file: {}'.format(args.file))
logger.info('Loading config file: {}'.format(args.file))
try:
exporters = list(args.exporter or ['default', ])
@ -68,6 +73,10 @@ def main():
exp_params = {}
if args.dry_run:
exp_params['copy_to'] = sys.stdout
if not os.path.exists(args.file):
logger.error('Please, input a valid file')
return
simulation.run_from_config(args.file,
dry_run=args.dry_run,
exporters=exporters,

@ -14,15 +14,6 @@ from .utils import open_or_reuse, logger, timer
from . import utils
def for_sim(simulation, names, *args, **kwargs):
'''Return the set of exporters for a simulation, given the exporter names'''
exporters = []
for name in names:
mod = deserialize(name, known_modules=['soil.exporters'])
exporters.append(mod(simulation, *args, **kwargs))
return exporters
class DryRunner(BytesIO):
def __init__(self, fname, *args, copy_to=None, **kwargs):
super().__init__(*args, **kwargs)
@ -38,8 +29,12 @@ class DryRunner(BytesIO):
super().write(bytes(txt, 'utf-8'))
def close(self):
logger.info('**Not** written to {} (dry run mode):\n\n{}\n\n'.format(self.__fname,
self.getvalue().decode()))
content = '(binary data not shown)'
try:
content = self.getvalue().decode()
except UnicodeDecodeError:
pass
logger.info('**Not** written to {} (dry run mode):\n\n{}\n\n'.format(self.__fname, content))
super().close()
@ -99,6 +94,12 @@ class default(Exporter):
with self.output('{}.sqlite'.format(env.name), mode='wb') as f:
env.dump_sqlite(f)
def end(self, stats):
with timer('Dumping simulation {}\'s stats'.format(self.simulation.name)):
with self.output('{}.sqlite'.format(self.simulation.name), mode='wb') as f:
self.simulation.dump_sqlite(f)
class csv(Exporter):
'''Export the state of each environment (and its agents) in a separate CSV file'''

@ -208,3 +208,13 @@ def deserialize(type_, value=None, **kwargs):
if value is None:
return des
return des(value)
def deserialize_all(names, *args, known_modules=['soil'], **kwargs):
'''Return the set of exporters for a simulation, given the exporter names'''
exporters = []
for name in names:
mod = deserialize(name, known_modules=known_modules)
exporters.append(mod(*args, **kwargs))
return exporters

@ -15,7 +15,7 @@ import pickle
from . import serialization, utils, basestring, agents
from .environment import Environment
from .utils import logger
from .exporters import default, for_sim as exporters_for_sim
from .exporters import default
from .stats import defaultStats
from .history import History
@ -133,7 +133,7 @@ class Simulation:
self.topology)
self._history = History(name=self.name,
backup=False)
backup=False)
def run_simulation(self, *args, **kwargs):
return self.run(*args, **kwargs)
@ -167,14 +167,16 @@ class Simulation:
logger.setLevel(log_level)
logger.info('Using exporters: %s', exporters or [])
logger.info('Output directory: %s', outdir)
exporters = exporters_for_sim(self,
exporters,
dry_run=dry_run,
outdir=outdir,
**exporter_params)
stats = exporters_for_sim(self,
stats,
**stats_params)
exporters = serialization.deserialize_all(exporters,
simulation=self,
known_modules=['soil.exporters',],
dry_run=dry_run,
outdir=outdir,
**exporter_params)
stats = serialization.deserialize_all(simulation=self,
names=stats,
known_modules=['soil.stats',],
**stats_params)
with utils.timer('simulation {}'.format(self.name)):
for stat in stats:
@ -293,6 +295,9 @@ class Simulation:
with utils.open_or_reuse(f, 'wb') as f:
pickle.dump(self, f)
def dump_sqlite(self, f):
return self._history.dump(f)
def __getstate__(self):
state={}
for k, v in self.__dict__.items():

@ -74,10 +74,10 @@ class Exporters(TestCase):
s = simulation.from_config(config)
tmpdir = tempfile.mkdtemp()
envs = s.run_simulation(exporters=[
exporters.default,
exporters.csv,
exporters.gexf,
],
exporters.default,
exporters.csv,
exporters.gexf,
],
stats=[distribution,],
outdir=tmpdir,
exporter_params={'copy_to': output})

Loading…
Cancel
Save