Index: framspy/evolalg/base/union_step.py
===================================================================
--- framspy/evolalg/base/union_step.py	(revision 1145)
+++ framspy/evolalg/base/union_step.py	(revision 1146)
@@ -12,4 +12,9 @@
             self.steps = [steps]
 
+    def init(self):
+        for s in self.steps:
+            if isinstance(s, Step):
+                s.init()
+
     def call(self, population):
         super(UnionStep, self).call(population)
Index: framspy/evolalg/examples/niching_novelty.py
===================================================================
--- framspy/evolalg/examples/niching_novelty.py	(revision 1145)
+++ framspy/evolalg/examples/niching_novelty.py	(revision 1146)
@@ -141,14 +141,14 @@
 
 
+def func_raw(ind): setattr(ind, "fitness", ind.fitness_raw)
+
+
+def func_novelty(ind): setattr(ind, "fitness", ind.dissim)
+
+
+def func_knn_novelty(ind): setattr(ind, "fitness", ind.dissim)
+
+
 def func_niching(ind): setattr(ind, "fitness", ind.fitness_raw * (1 + ind.dissim))
-
-
-def func_raw(ind): setattr(ind, "fitness", ind.fitness_raw)
-
-
-def func_novelty(ind): setattr(ind, "fitness", ind.dissim)
-
-
-def func_knn_novelty(ind): setattr(ind, "fitness", ind.dissim)
 
 
@@ -180,5 +180,5 @@
     new_generation_stages = [FramsCrossAndMutate(frams_lib, cross_prob=0.2, mutate_prob=0.9)]
 
-    # Steps after new population is created. Executed exacly once per generation.
+    # Steps after new population is created. Executed exactly once per generation.
     generation_modifications = []
 
@@ -245,5 +245,5 @@
         generation_modifications.append(raw)
 
-    if parsed_args.fit == Fitness.niching:
+    if parsed_args.fit == Fitness.niching: # TODO reduce redundancy in the four cases below: dictionary?
         niching = UnionStep([
             dissim,
@@ -297,5 +297,5 @@
 
     # -------------------------------------------------
-    # End stages: this will execute exacly once after all generations.
+    # End stages: this will execute exactly once after all generations.
     end_stages = [
         replace_with_hof,
Index: framspy/evolalg/examples/standard.py
===================================================================
--- framspy/evolalg/examples/standard.py	(revision 1145)
+++ framspy/evolalg/examples/standard.py	(revision 1146)
@@ -8,5 +8,5 @@
 # TODO add comments to all examples in this directory
 # TODO add to standard.py and steadystate.py evaluating each genotype in HOF N (configurable, default 20) times when the evolution ends, as it is in niching_novelty.py
-# TODO "--debug" mode, indent nested steps (pre++, post-- of a static counter?) and print their arguments so it is easy to see what happens during evolution
+# TODO "-debug" mode, indent nested steps (pre++, post-- of a static counter?) and print their arguments so it is easy to see what happens during evolution
 
 
@@ -80,10 +80,10 @@
     ])
 
-    fitness_remove = UnionStep(
+    fitness_remove = UnionStep(  # evaluate performance and fitness, rename some of the fields, and remove some performance fields that we get from Framsticks, but we don't need them here
         [
         FitnessStep(frams_lib, fields={"velocity": "fitness", "data->recording": "recording"},
                     fields_defaults={"velocity": None, "data->recording": None})  # custom definitions and handling
         if EVAL_LIFESPAN_BEHAVIOR else
-        FitnessStep(frams_lib, fields={parsed_args.opt: "fitness", }, fields_defaults={parsed_args.opt: None})
+        FitnessStep(frams_lib, fields={parsed_args.opt: "fitness"}, fields_defaults={parsed_args.opt: None})
         ]
         +
Index: framspy/evolalg/statistics/statistics_deap.py
===================================================================
--- framspy/evolalg/statistics/statistics_deap.py	(revision 1145)
+++ framspy/evolalg/statistics/statistics_deap.py	(revision 1146)
@@ -26,2 +26,7 @@
         if self.verbose:
             print(self.logbook.stream)
+
+    def compile(self, data):
+        return self.stats.compile(data)
+
+
Index: framspy/frams.py
===================================================================
--- framspy/frams.py	(revision 1145)
+++ framspy/frams.py	(revision 1146)
@@ -14,4 +14,7 @@
 
 For sample usage, see frams-test.py and FramsticksLib.py.
+
+If you want to run many independent instances of this class in parallel, use the "multiprocessing" module and then each process
+that uses this module will initialize it and get access to a separate instance of the Framsticks library.
 
 For interfaces in other languages (e.g. using the Framsticks library in your C++ code), see ../cpp/frams/frams-objects.h
@@ -285,5 +288,6 @@
 
 	original_dir = os.getcwd()
-	os.chdir(lib_path)  # because under Windows, frams-objects.dll requires other dll's which reside in the same directory, so we must change current dir for them to be found while loading the main dll
+	os.chdir(lib_path)  # because under Windows, frams-objects.dll requires other dll's which reside in the same directory, so we must change current dir for them to be found while loading the main dll.
+	# TODO in python 3.8+ and Windows, use os.add_dll_directory(lib_path) instead of os.chdir()? And maybe for linux we no longer need chdir() and "./", but use absolute path?
 	abs_data = os.path.abspath('data')  # use absolute path for -d and -D so python is free to cd anywhere without confusing Framsticks
 	# for the hypothetical case without lib_path the abs_data must be obtained from somewhere else
@@ -298,7 +302,7 @@
 	global c_api  # access global variable
 	if lib_path is not None and os.name == 'posix':
-		lib_name = './' + lib_name  # currently we always have lib_path (even if it is incorrect) but hypothetically it could work with lib_path==None and load .so from some default system path without './'
+		lib_name = './' + lib_name  # currently we always have lib_path (even if it is incorrect), but hypothetically it could work with lib_path==None and then load .so from some default system path without './'
 	try:
-		c_api = ctypes.CDLL(lib_name)  # should be a separate instance and no globals if we want multiple threads to use this module (todo?)
+		c_api = ctypes.CDLL(lib_name)  # if accessing this module from multiple threads, they will all share a single c_api and access the same copy of the library and its data. If you want separate independent copies, read the comment at the top of this file on using the "multiprocessing" module.
 	except OSError as e:
 		print("*** Could not find or open '%s' from '%s'.\n*** Did you provide proper arguments and is this file readable?\n" % (lib_name, os.getcwd()))
