From 61eb8c84956a9bb0bd65c65b4c71caf5a9d27446 Mon Sep 17 00:00:00 2001
From: David Hendriks <davidhendriks93@gmail.com>
Date: Sat, 25 Jan 2020 01:11:15 +0000
Subject: [PATCH] Added functionalities around grid code generation, like
 accepting custom defined functions, exporting it to a file and saving it to
 grid_options

---
 binarycpython/utils/grid.py                   |  36 +-
 binarycpython/utils/grid_options_defaults.py  | 474 ++++++++++++++++++
 .../utils/probability_distributions.py        | 256 +++++++++-
 tests/population/grid_tests.py                |  14 +-
 4 files changed, 762 insertions(+), 18 deletions(-)

diff --git a/binarycpython/utils/grid.py b/binarycpython/utils/grid.py
index 654841131..bc35f34e4 100644
--- a/binarycpython/utils/grid.py
+++ b/binarycpython/utils/grid.py
@@ -471,30 +471,28 @@ class Population(object):
         # TODO: make a generator for this.  
         # TODO: Add correct logging everywhere
         # TODO: add different types of grid. 
-        # TODO: Load the generated file and execute.
+        # TODO: add part to handle separation if orbital_period is added
         """
 
+
+        # Some local values
         code_string = ""
         depth = 0
         indent = '    '
 
-        # Set some values in the generated code:
-
-        # TODO: add imports
-        # TODO: 
-
-        #
+        # Import packages
         code_string += "from binarycpython.utils.probability_distributions import *\n"  
         code_string += "import math\n"
         code_string += "import numpy as np\n"
         code_string += "\n\n"
 
+        # Make the function
+        code_string += "def grid_code(self):\n"
 
-        code_string += "def grid_code():\n"
-
+        # Increase depth 
         depth += 1
 
-        # 
+        # Set some values in the generated code:
         code_string += indent*depth + "starcount = 0\n"
         code_string += indent*depth + "probabilities = {}\n"
         code_string += indent*depth + "parameter_dict = {}\n"
@@ -510,6 +508,15 @@ class Population(object):
             print("Constructing/adding: {}".format(el[0]))
             grid_variable = el[1]
 
+            # If the grid variable has a condition, write the check and the action
+            if grid_variable['condition']:
+                # Add condition check
+                code_string += indent * depth + 'if not {}:'.format(grid_variable['condition']) + '\n'
+
+                # Add condition failed action: #TODO: add correct exception error
+                code_string += indent * (depth + 1) + 'print("Condition not met!")'.format(grid_variable['condition']) + '\n'            
+                code_string += indent * (depth + 1) + 'raise ValueError'.format(grid_variable['condition']) + '\n'
+
             # Adding for loop structure
             code_string += indent * depth + 'for {} in {}:'.format(grid_variable['name'], grid_variable['spacingfunc']) + '\n'
 
@@ -539,8 +546,13 @@ class Population(object):
         code_string += indent * (depth) + 'print("starcount: ", starcount)\n'
         code_string += indent * (depth) + 'yield(parameter_dict)\n'
 
+        # Save the gridcode to the grid_options
+        self.grid_options['code_string'] = code_string
+
         # Write to file
         gridcode_filename = os.path.join(temp_custom_logging_dir(), 'example_grid.py')
+        self.grid_options['gridcode_filename'] = gridcode_filename
+
         with open(gridcode_filename, 'w') as f:
             f.write(code_string)
 
@@ -548,11 +560,13 @@ class Population(object):
         """
         Test function to run grid stuff. mostly to test the import
         """
+
+        # Code to load the 
         import importlib.util
         spec = importlib.util.spec_from_file_location("binary_c_python_grid", os.path.join(temp_custom_logging_dir(), 'example_grid.py'))
         grid_file = importlib.util.module_from_spec(spec)
         spec.loader.exec_module(grid_file)
-        generator = grid_file.grid_code()
+        generator = grid_file.grid_code(self)
 
 
         print(next(generator))
diff --git a/binarycpython/utils/grid_options_defaults.py b/binarycpython/utils/grid_options_defaults.py
index fd62de48a..8ff1a0c32 100644
--- a/binarycpython/utils/grid_options_defaults.py
+++ b/binarycpython/utils/grid_options_defaults.py
@@ -15,6 +15,10 @@ grid_options_defaults_dict = {
     "log_args_dir": "/tmp/",
     # Grid variables: instructions to generate the values of the parameters
     "grid_variables": {},
+    "grid_code": None,
+    "gridcode_filename": None,
+    # binary
+    "binary": 0,
     ##
     # return_array_refs=>1, # quicker data parsing mode
     # sort_args=>1,
@@ -23,4 +27,474 @@ grid_options_defaults_dict = {
     # timeout=>15, # seconds until timeout
     # log_filename=>"/scratch/davidh/results_simulations/tmp/log.txt",
     # # current_log_filename=>"/scratch/davidh/results_simulations/tmp/grid_errors.log",
+
+
+
+
+
+#     my $self = shift;
+
+    ############################################################
+    # Set default grid properties (in %self->{_grid_options}} 
+    # and %{$self->{_bse_options}})
+    # This is the first thing that should be called by the user!
+    ############################################################
+
+    # # set signal handlers for timeout
+    # $self->set_class_signal_handlers();
+
+    # # set operating system
+    # my $os = rob_misc::operating_system();
+    
+    # # set tmp dir
+    # my $tmp = $self->tmpdir($os);
+     
+    # %{$self->{_grid_options}}=(
+
+    #     # save operating system
+    # operating_system=>$os,
+
+    #     # process name
+    #     process_name => 'binary_grid'.$VERSION,
+        
+    # # temp directory: 
+    # tmp=>$tmp,
+
+    # grid_defaults_set=>1, # so we know the grid_defaults function has been called
+
+    # # grid suspend files: assume binary_c by default
+    # suspend_files=>[$tmp.'/force_binary_c_suspend',
+    #         './force_binary_c_suspend'],
+
+    # snapshot_file=>$tmp.'/binary_c-snapshot',
+    
+
+    # ########################################
+    # # infomration about the running grid script
+    # ########################################
+    # working_directory=>cwd(), # the starting directory
+    # perlscript=>$0, # the name of the perlscript
+    # perlscript_arguments=>join(' ',@ARGV), # arguments as a string
+    # perl_executable=>$^X, # the perl executable
+    # command_line=>join(' ',$0,@ARGV), # full command line
+    # process_ID=>$$, # process ID of the main perl script
+
+    # ########################################
+    # # GRID
+    # ########################################
+
+    #     # if undef, generate gridcode, otherwise load the gridcode
+    #     # from this file. useful for debugging
+    #     gridcode_from_file => undef,
+        
+    #     # assume binary_grid perl backend by default
+    #     backend => 
+    #     $self->{_grid_options}->{backend} // 
+    #     $binary_grid2::backend //
+    #     'binary_grid::Perl',
+
+    #     # custom C function for output : this automatically
+    #     # binds if a function is available.
+    #     C_logging_code => undef,
+    #     C_auto_logging => undef,
+    #     custom_output_C_function_pointer => binary_c_function_bind(), 
+        
+    # # control flow
+    # rungrid=>1, # usually run the grid, but can be 0
+    # # to skip it (e.g. for condor/slurm runs)
+    # merge_datafiles=>'',
+    # merge_datafiles_filelist=>'',
+
+    # # parameter space options
+    # weight=>1.0, # weighting for the probability
+
+    # repeat=>1, # number of times to repeat each system (probability is adjusted to be 1/repeat)
+
+    # binary=>0, # set to 0 for single stars, 1 for binaries     
+
+    #     # if use_full_resolution is 1, then run a dummy grid to
+    #     # calculate the resolution. this could be slow...
+    #     use_full_resolution => 1,
+        
+    # # the probability in any distribution must be within
+    # # this tolerance of 1.0, ignored if undef (if you want
+    # # to run *part* of the parameter space then this *must* be undef)
+    # probability_tolerance=>undef,
+
+    # # how to deal with a failure of the probability tolerance:
+    # # 0 = nothing
+    # # 1 = warning
+    # # 2 = stop
+    # probability_tolerance_failmode=>1,
+
+    # # add up and log system error count and probability
+    # add_up_system_errors=>1,
+    # log_system_errors=>1,
+
+    # # codes, paths, executables etc.
+    
+    # # assume binary_c by default, and set its defaults
+    # code=>'binary_c',
+    # arg_prefix=>'--',
+    # prog=>'binary_c', # executable 
+    # nice=>'nice -n +0', # nice command
+    # ionice=>'',
+        
+    # # compress output?
+    # binary_c_compression=>0,
+
+    #     # get output as array of pre-split array refs
+    #     return_array_refs=>1,
+
+    # # environment
+    # shell_environment=>undef,
+    # libpath=>undef, # for backwards compatibility
+
+    # # where is binary_c? need this to get the values of some counters
+    # rootpath=>$self->okdir($ENV{BINARY_C_ROOTPATH}) // 
+    # $self->okdir($ENV{HOME}.'/progs/stars/binary_c') //
+    # '.' , # last option is a fallback ... will fail if it doesn't exist
+
+    # srcpath=>$self->okdir($ENV{BINARY_C_SRCPATH}) // 
+    # $self->okdir($ENV{BINARY_C_ROOTPATH}.'/src') // 
+    # $self->okdir($ENV{HOME}.'/progs/stars/binary_c/src') //
+    # './src' , # last option is fallback... will fail if it doesn't exist
+
+    # # stack size per thread in megabytes
+    # threads_stack_size=>50, 
+
+    # # thread sleep time between starting the evolution code and starting 
+    # # the grid
+    # thread_presleep=>0,
+
+    # # threads
+    # # Max time a thread can sit looping (with calls to tbse_line)
+    # # before a warning is issued : NB this does not catch real freezes,
+    # # just infinite loops (which still output)
+    # thread_max_freeze_time_before_warning=>10,
+    
+    # # run all models by default: modulo=1, offset=0
+    # modulo=>1,
+    # offset=>0,      
+
+    #     # max number of stars on the queue
+    #     maxq_per_thread => 100,
+        
+    # # data dump file : undef by default (do nothing)
+    # results_hash_dumpfile => '',
+
+    # # compress files with bzip2 by default
+    # compress_results_hash => 1,
+
+    # ########################################
+    # # Condor stuff
+    # ########################################    
+    # condor=>0, # 1 to use condor, 0 otherwise
+    #     condor_command=>'',# condor command e.g. "run_flexigrid", 
+    # # "join_datafiles"
+    # condor_dir=>'', # working directory containing e.g.
+    # # scripts, output, logs (e.g. should be NFS available to all)
+    # condor_njobs=>'', # number of scripts
+    # condor_jobid=>'', # condor job id
+    # condor_postpone_join=>0, # if 1, data is not joined, e.g. if you
+    # # want to do it off the condor grid (e.g. with more RAM)
+    # condor_join_machine=>undef, # if defined then this is the machine on which the join command should be launched (must be sshable and not postponed)
+    # condor_join_pwd=>undef, # directory the join should be in
+    # # (defaults to $ENV{PWD} if undef)
+    # condor_memory=>1024, # in MB, the memory use (ImageSize) of the job
+    # condor_universe=>'vanilla', # usually vanilla universe
+    # condor_snapshot_on_kill=>0, # if 1 snapshot on SIGKILL before exit
+    # condor_load_from_snapshot=>0, # if 1 check for snapshot .sv file and load it if found
+    # condor_checkpoint_interval=>0, # checkpoint interval (seconds) 
+    # condor_checkpoint_stamp_times=>0, # if 1 then files are given timestamped names (warning: lots of files!), otherwise just store the lates
+    # condor_streams=>0, # stream stderr/stdout by default (warning: might cause heavy network load)
+    # condor_save_joined_file=>0, # if 1 then results/joined contains the results (useful for debugging, otherwise a lot of work)
+    # condor_requirements=>'', # used?
+
+    #     # resubmit options : if the status of a condor script is
+    #     # either 'finished','submitted','running' or 'crashed',
+    #     # decide whether to resubmit it.
+    #     # NB Normally the status is empty, e.g. on the first run.
+    #     # These are for restarting runs.
+    #     condor_resubmit_finished=>0,
+    # condor_resubmit_submitted=>0,
+    # condor_resubmit_running=>0,
+    # condor_resubmit_crashed=>0,
+
+
+    # ########################################
+    # # Slurm stuff
+    # ########################################    
+    #     slurm=>0, # don't use slurm by default
+    # slurm_command=>'',# slurm command e.g. "run_flexigrid", 
+    # # "join_datafiles"
+    # slurm_dir=>'', # working directory containing e.g.
+    # # scripts, output, logs (e.g. should be NFS available to all)
+    # slurm_njobs=>'', # number of scripts
+    # slurm_jobid=>'', # slurm job id (%A)
+    # slurm_jobarrayindex=>'', # slurm job array index (%a)
+    #     slurm_jobname=>'binary_grid', # set to binary_grid
+    #     slurm_postpone_join=>0, # if 1, data is not joined, e.g. if you
+    # # want to do it off the slurm grid (e.g. with more RAM)
+    #     slurm_postpone_sbatch=>0, # if 1, don't submit, just make the script
+    
+    # # (defaults to $ENV{PWD} if undef)
+    # slurm_memory=>512, # in MB, the memory use of the job
+    #     slurm_warn_max_memory=>1024, # in MB : warn if mem req. > this
+    #     slurm_partition=>undef,
+    #     slurm_ntasks=>1, # 1 CPU required per array job: usually only need this
+    #     slurm_time=>0, # 0 = infinite time
+    # slurm_use_all_node_CPUs=>0, # 1 = use all of a node's CPUs (0)
+    # # you will want to use this if your Slurm SelectType is e.g. linear
+    # # which means it allocates all the CPUs in a node to the job
+    # slurm_control_CPUs=>0, # if so, leave this many for Perl control (0)
+    #     slurm_array=>undef,# override for --array, useful for rerunning jobs
+        
+    # ########################################
+    # # CPU 
+    # ########################################
+    # cpu_cap=>0, # if 1, limits to one CPU
+    # cpu_affinity => 0, # do not bind to a CPU by default
+
+    # ########################################
+    # # Code, Timeouts, Signals
+    # ########################################
+    # binary_grid_code_filtering=>1, #  you want this, it's (MUCH!) faster
+    # pre_filter_file=>undef, # dump pre filtered code to this file
+    # post_filter_file=>undef,  # dump post filtered code to this file
+
+    # timeout=>30, # timeout in seconds
+    # timeout_vb=>0, # no timeout logging
+    # tvb=>0, # no thread logging
+    # nfs_sleep=>1, # time to wait for NFS to catch up with file accesses
+
+    # # flexigrid checks the timeouts every 
+    # # flexigrid_timeout_check_interval seconds
+    # flexigrid_timeout_check_interval=>0.01, 
+
+    # # this is set to 1 when the grid is finished
+    # flexigrid_finished=>0,
+
+    # # allow signals by default
+    # 'no signals'=>0,
+
+    # # but perhaps disable specific signals?
+    # 'disable signal'=>{INT=>0,ALRM=>0,CONT=>0,USR1=>0,STOP=>0},
+
+    # # dummy variables
+    # single_star_period=>1e50,  # orbital period of a single star
+
+    # #### timers : set timers to 0 (or empty list) to ignore, 
+    # #### NB these must be given context (e.g. main::xyz) 
+    # #### for functions not in binary_grid
+    # timers=>0,
+    # timer_subroutines=>[
+    #     # this is a suggested default list
+    #     'flexigrid',
+    #         'set_next_alarm',
+    #     'vbout',
+    #         'vbout_fast',
+    #     'run_flexigrid_thread',
+    #         'thread_vb'
+    # ],
+
+    # ########################################
+    # # INPUT/OUTPUT
+    # ########################################
+    # blocking=>undef, # not yet set
+    
+    # # prepend command with stdbuf to stop buffering (if available)
+    # stdbuf_command=>`stdbuf --version`=~/stdbuf \(GNU/ ? ' stdbuf -i0 -o0 -e0 ' : undef,
+
+    # vb=>("@ARGV"=~/\Wvb=(\d+)\W/)[0] // 0, # set to 1 (or more) for verbose output to the screen
+    # log_dt_secs=>1, # log output to stdout~every log_dt_secs seconds
+    # nmod=>10, # every nmod models there is output to the screen,
+    # # if log_dt_secs has been exceeded also (ignored if 0)
+
+    # colour=>1, # set to 1 to use the ANSIColor module for colour output
+    # log_args=>0, # do not log args in files
+    # log_fins=>0, # log end of runs too
+    #     sort_args=>0, # do not sort args
+    # save_args=>0, # do not save args in a string
+    # log_args_dir=>$tmp, # where to output the args files
+    # always_reopen_arg_files=>0, # if 1 then arg files are always closed and reopened (may cause a lot of disk I/O)
+
+    # lazy_arg_sending=>1, # if 1, the previous args are remembered and
+    # # only args that changed are sent (except M1, M2 etc. which always
+    # # need sending)
+ 
+    # # force output files to open on a local disk (not an NFS partion)
+    # # not sure how to do this on another OS
+    # force_local_hdd_use=>($os eq 'unix'), 
+
+    # # for verbose output, define the newline
+    # # For terminals use "\x0d", for files use "\n", in the 
+    # # case of multiple threads this will be set to \n
+    # newline=> "\x0d",
+
+    #     # use reset_stars_defaults
+    #     reset_stars_defaults=>1,
+
+    # # set signal captures: argument determines behaviour when the code locks up
+    # # 0: exit
+    # # 1: reset and try the next star (does this work?!)
+    # alarm_procedure=>1,
+
+    # # exit on eval failure?
+    # exit_on_eval_failure=>1,
+
+    # ## functions: these should be set by perl lexical name
+    # ## (they are automatically converted to function pointers
+    # ## at runtime)
+
+    # # function to be called just before a thread is created
+    # thread_precreate_function=>undef,
+    #     thread_precreate_function_pointer=>undef,
+    
+    # # function to be called just after a thread is created
+    # # (from inside the thread just before *grid () call)
+    # threads_entry_function=>undef,
+    #     threads_entry_function_pointer=>undef,
+
+    # # function to be called just after a thread is finished
+    # # (from inside the thread just after *grid () call)
+    # threads_flush_function=>undef,
+    # threads_flush_function_pointer=>undef,
+
+    # # function to be called just after a thread is created
+    # # (but external to the thread)
+    # thread_postrun_function=>undef,
+    # thread_postrun_function_pointer=>undef,
+
+    # # function to be called just before a thread join
+    # # (external to the thread)
+    # thread_prejoin_function=>undef,
+    # thread_prejoin_function_pointer=>undef,
+
+    # # default to using the internal join_flexigrid_thread function
+    # threads_join_function=>'binary_grid2::join_flexigrid_thread',
+    # threads_join_function_pointer=>sub{return $self->join_flexigrid_thread(@_)},
+    
+    # # function to be called just after a thread join
+    # # (external to the thread)
+    # thread_postjoin_function=>undef,
+    # thread_postjoin_function_pointer=>undef,
+
+    # # usually, parse_bse in the main script is called
+    # parse_bse_function=>'main::parse_bse',
+    #     parse_bse_function_pointer=>undef,
+
+    # # if starting_snapshot_file is defined, load initial
+    # # values for the grid from the snapshot file rather 
+    # # than a normal initiation: this enables you to 
+    # # stop and start a grid
+    # starting_snapshot_file=>undef,
+    
+    # # flexigrid options
+    # flexigrid=>{
+    #         # there are several types of flexigrid:
+    #         #
+    #         # 'grid' is the traditional N-dimensional grid
+    #         # 'monte carlo' is a Monte Carlo simulation (may not work!)
+    #         # 'list' takes a list of systems from 
+    #         #
+    #         # $self->{_grid_options}->{flexigrid}->{'list filename'}
+    #         #
+    #         # (which is a file containing list strings on each line)
+    #         #
+    #         # or from 
+    #         # 
+    #         # $self->{_grid_options}->{flexigrid}->{'list reference'}
+    #         #
+    #         # (which is a reference to a perl list)
+    #     'grid type'=>'grid',
+    #         'list filename'=>undef,  # undef unless 'grid type' is 'list'
+    #         'list reference'=>undef, # ditto
+    #         'listFP'=>undef, # file pointer : keep undef here (set automatically) 
+
+    #     },
+
+    #     # start at this model number: handy during debugging 
+    #     # to skip large parts of the grid
+    #     start_at => 0
+    # );
+    # );
+
+    # # if available, use the evcode's defaults
+    # if(1)
+    # {
+    #     my $evcode_args = $self->evcode_args_list();
+        
+    #     if(defined $evcode_args && 
+    #        $evcode_args &&
+    #        ref $evcode_args eq 'ARRAY' &&
+    #        $#{$evcode_args} > 1)
+    #     {
+    #         foreach my $arg (grep {
+    #             !(
+    #                  # some args should be ignored
+    #                  /=\s*$/ ||
+    #                  /Function$/ ||
+    #                  /NULL$/ ||
+    #                  /\(?null\)?$/i ||
+    #                  /^M_[12]/ ||
+    #                  /^eccentricity/ ||
+    #                  /^orbital_period/ ||
+    #                  /^phasevol/ ||
+    #                  /^separation/ ||
+    #                  /^probability/ ||
+    #                  /^stellar_type/ ||
+    #                  /^_/||
+    #                  /^batchmode/  ||
+    #                  /^initial_abunds_only/ ||
+    #                  /^monte_carlo_kicks/
+    #                 )
+    #                          }@$evcode_args)
+    #         {
+    #             if($arg=~/(\S+) = (\S+)/)
+    #             {
+    #                 if(!defined $self->{_bse_options}->{$1})
+    #                 {
+    #                     #print "NEW set $1 to $2\n";
+    #                 }
+    #                 $self->{_bse_options}->{$1} = 
+    #                     $2 eq 'TRUE' ? 1 :
+    #                     $2 eq 'FALSE' ? 0 :
+    #                     $2;
+    #                 #print "Set $1 -> $2 = $self->{_bse_options}->{$1}\n";
+    #             }
+    #         }
+    #     }
+    # }
+   
+    # $self->{_flexigrid} = {
+    # count         => 0,
+    #     error         => 0,
+    #     failed_count  => 0,
+    #     failed_prob   => 0.0,
+    #     global_error_string => undef,
+
+    #     # random string to ID the flexigrid
+    #     id            => rob_misc::random_string(8),
+    #     modulo        => 1, # run modulo n
+    #     monitor_files => [],
+    #     nextlogtime   => 0,
+    #     nthreads      => 1, # number of threads
+    #     # start at model offset (0-based, so first model is zero)
+    #     offset        => 0, 
+    #     probtot       => 0.0,
+    #     resolution=>{
+    #         shift   =>0,
+    #         previous=>0,
+    #         n       =>{} # per-variable resolution
+    #     },
+    #     results_hash  => $self->{_results},
+    #     thread_q      => undef,
+    #     threads       => undef, # array of threads objects
+    #     tstart        => [gettimeofday], # flexigrid start time     
+    #     __nvar        => 0, # number of grid variables
+    #     _varstub      => undef,
+    #     _lock         => undef,
+    #     _evcode_pids  => [],
+    # }; 
 }
diff --git a/binarycpython/utils/probability_distributions.py b/binarycpython/utils/probability_distributions.py
index 57f8b6b32..0c6193085 100644
--- a/binarycpython/utils/probability_distributions.py
+++ b/binarycpython/utils/probability_distributions.py
@@ -1,2 +1,256 @@
+
+# TODO: make some things globally present? rob does this in his module. i guess it saves calculations but not sure if im gonna do that now
+
 def flat(parameter):
-    return 1
\ No newline at end of file
+    """
+    Dummt distribution function that returns 1
+    """
+    return 1
+
+def number(value):
+    """
+    Dummy distribution function that returns the input
+    """
+    return value
+
+def powerlaw_constant(min_val, max_val, k):
+    """
+    Function that returns the constant to normalise a powerlaw
+    """
+
+    k1 = k + 1.0
+    print("Powerlaw consts from {} to {}, k={} where k1={}".format(min_val, max_val, k, k1))
+
+    powerlaw_const = k1/(max_val**k1 - min_val**k1);
+    return powerlaw_const
+
+def powerlaw(min_val, max_val, k, x):
+    """
+    Single powerlaw with index k at x from min to max
+    """
+
+    # Handle faulty value
+    if k == -1:
+        print("wrong value for k")
+        raise ValueError
+
+    if (x < min_val) or (x > max_val):
+        return 0
+
+    else:
+        const = powerlaw_constant(min_val, max_val, k)
+
+        # powerlaw
+        y = const * (x ** k)
+        print("Power law from {} to {}: const = {}, y = {}".format(min_val, max_val, const, y))
+        return y
+    
+
+# sub const
+# {
+#     # a constant distribution function between min=$_[0] and max=$_[1]
+#     # $_[2] is optional (if given, perform a range check)
+    
+#     my $C= (defined($_[2]) && (($_[2]<$_[0]) || ($_[2]>$_[1]))) ? 0.0 : (1.0/($_[1]-$_[0]));
+#     #printf "CONST $_[0] to $_[1] ($_[2]) $C\n";
+#     return $C;
+# }
+    
+# sub initialize_three_part_power_law_consts
+# {
+#     local $SIG{__DIE__} = sub { Carp::confess @_ };
+#     # calculate constants for this power law
+#     my ($m0,$m1,$m2,$mmax,$p1,$p2,$p3)=@_;
+#     #print "INIT three-part power law m0=$m0 m1=$m1 m2=$m2 mmax=$mmax p1=$p1 p2=$p2 p3=$p3\n";
+#     my $array=[];
+
+#     $$array[1]=(($m1**$p2)*($m1**(-$p1)))*
+#     (1.0/(1.0+$p1))*
+#     ($m1**(1.0+$p1)-$m0**(1.0+$p1))+
+#     (($m2**(1.0+$p2)-$m1**(1.0+$p2)))*
+#     (1.0/(1.0+$p2))+
+#     (($m2**$p2)*($m2**(-$p3)))*
+#     (1.0/(1.0+$p3))*
+#     ($mmax**(1.0+$p3)-$m2**(1.0+$p3));
+#     $$array[1]=1.0/($$array[1]+1e-50);
+#     $$array[0]=$$array[1]*$m1**$p2*$m1**(-$p1);
+#     $$array[2]=$$array[1]*$m2**$p2*$m2**(-$p3);
+
+#     #print "ARRAY SET @_ => @$array\n";
+#     $threepart_powerlaw_consts{"@_"}=[@$array];
+# }
+
+# sub ktg93_lnspace
+# {
+#     # wrapper for KTG93 on a ln(m) grid
+#     my $m=$_[0];
+#     return ktg93(@_) * $m; 
+# }
+
+# sub setopts
+# {
+#     # wrapper to take a hash (reference) of options,
+#     # override with $newopts (where appropriate) and
+#     # return the hash of options
+#     my $opts=$_[0];
+#     my $newopts=$_[1];
+#     if(defined $newopts)
+#     { 
+#     # newopts is a hash of new options 
+#     foreach my $opt (keys %$newopts)
+#     {
+#         # overwrite opt
+#         $$opts{$opt} = $$newopts{$opt};
+#     }
+#     }
+#     return $opts;
+# }
+
+# sub Kroupa2001
+# {
+#     my $m=$_[0];
+#     my $newopts=$_[1];
+
+#     # default parameters
+#     my $opts=setopts({m0=>0.1,
+#               m1=>0.5,
+#               m2=>1.0,
+#               mmax=>100.0,
+#               p1=>-1.3,
+#               p2=>-2.3,
+#               p3=>-2.3},
+#              $newopts);
+
+#     return three_part_power_law($m,
+#                 $$opts{m0},
+#                 $$opts{m1},
+#                 $$opts{m2},
+#                 $$opts{mmax},
+#                 $$opts{p1},
+#                 $$opts{p2},
+#                 $$opts{p3});
+# }
+
+# sub ktg93
+# {
+#     # wrapper for the mass distribution of KTG93
+#     my ($m,$newopts)=@_;
+
+#     if($m eq 'uncertainties')
+#     {
+#     # return (pointer to) the uncertainties hash
+#     return {
+#         m0=>{default=>0.1,
+#          fixed=>1},
+#         m1=>{default=>0.5,
+#          fixed=>1},
+#         m2=>{default=>1.0,
+#          fixed=>1},
+#         mmax=>{default=>80.0,
+#            fixed=>1},
+#         p1=>{default=>-1.3,
+#          low=>-1.3,
+#          high=>-1.3},
+#         p2=>{default=>-2.2,
+#          low=>-2.2,
+#          high=>-2.2},
+#         p3=>{default=>-2.7,
+#          low=>-2.7,
+#          high=>-2.7}
+#     };
+#     }
+
+#     # set options
+#     my $opts=setopts({m0=>0.1,
+#               m1=>0.5,
+#               m2=>1.0,
+#               mmax=>80.0,
+#               p1=>-1.3,
+#               p2=>-2.2,
+#               p3=>-2.7},
+#              $newopts);
+
+#     return three_part_power_law($m,
+#                 $$opts{m0},
+#                 $$opts{m1},
+#                 $$opts{m2},
+#                 $$opts{mmax},
+#                 $$opts{p1},
+#                 $$opts{p2},
+#                 $$opts{p3});
+# }
+
+# sub three_part_power_law
+# {
+#     # generalized three part power law, usually used for mass distributions
+#     # args are M, M0, M1, M2, MMAX, P1, P2, P3 (powers)
+#     my $m=shift;
+
+#     # use pre-calculated consts if possible, otherwise calculate them
+#     my $consts=$threepart_powerlaw_consts{"@_"} //
+#     initialize_three_part_power_law_consts(@_);
+   
+#     my $p;
+#     if($m<$_[1])
+#     { 
+#     $p = $m<$_[0]
+#        ? 0.0
+#        : $$consts[0]*($m**$_[4]);
+#     }
+#     elsif($m<$_[3])
+#     {
+#     $p = $m<$_[2]
+#         ? $$consts[1]*($m**$_[5])
+#         : $$consts[2]*($m**$_[6]);
+#     }
+#     else
+#     {
+#     $p=0.0;
+#     }
+#     return $p;
+# }
+
+# sub gaussian
+# {
+#     # Gaussian distribution function e.g. for Duquennoy + Mayor 1991
+
+#     # location (X value), mean and sigma, min and max range
+#     my ($x,$mean,$sigma,$gmin,$gmax) = @_;
+ 
+#     my $p;
+#     if($x<$gmin || $x>$gmax)
+#     {
+#     $p=0.0;
+#     }
+#     else
+#     {
+#     # normalize over given range
+#     my $mult= $gauss_consts{$mean}{$sigma} //
+#         gaussian_normalizing_const($mean,$sigma,$gmin,$gmax);
+#     $p = $mult * gaussian_func($x,$mean,$sigma);
+#     }
+#     return $p;
+# }
+
+# sub gaussian_normalizing_const
+# {
+#     # first time: calculate multiplier for given $mean, $sigma
+#     my ($mean,$sigma,$gmin,$gmax) = @_;
+#     my $ptot=0.0;
+#     my $d=($gmax-$gmin)/1000.0;
+#     for(my $y=$gmin;$y<=$gmax;$y+=$d)
+#     {
+#     $ptot += $d * gaussian_func($y,$mean,$sigma);
+#     }
+#     $gauss_consts{$mean}{$sigma}=$ptot; # save for later
+#     return $gauss_consts{$mean}{$sigma};
+# }
+
+# sub gaussian_func
+# {
+#     # local function
+#     # three args: x, mean, sigma
+#     my $r=1.0/$_[2]; # 1/sigma
+#     my $y=($_[0]-$_[1])*$r;
+#     return(GAUSSIAN_PREFACTOR*$r*exp(-0.5*$y*$y));
+# }
diff --git a/tests/population/grid_tests.py b/tests/population/grid_tests.py
index 3404b2dbd..3d1ba1412 100644
--- a/tests/population/grid_tests.py
+++ b/tests/population/grid_tests.py
@@ -11,8 +11,6 @@ from binarycpython.utils.grid import Population
 from binarycpython.utils.functions import get_help_all, get_help
 
 ## Script is intended for some testing of grid functionality. Its a bit random, not really structured tbh
-
-
 test_pop = Population()
 
 ## Setting values
@@ -202,6 +200,10 @@ test_pop.set(
 # plt.savefig('sizes_for_commands.png')
 # plt.show()
 
+# def ding(val):
+#     return val
+# test_pop.set(extra_prob_function=ding)
+
 ### Grid generating testing
 test_pop.add_grid_variable(
     name='lnm1',
@@ -211,6 +213,7 @@ test_pop.add_grid_variable(
     spacingfunc='np.linspace(0.213, 10.2, 10)',
     precode='M_1=math.exp(lnm1)',
     probdist='flat(M_1)',
+    # probdist='self.custom_options["extra_prob_function"](M_1)',
     dphasevol='',
     parameter_name='M_1',
     condition='',
@@ -227,12 +230,11 @@ test_pop.add_grid_variable(
     probdist='flat(orbital_period)',
     parameter_name='orbital_period',
     dphasevol='',
-    condition='',
+    condition='self.grid_options["binary"]==0',
 )
 
 test_pop.generate_grid_code()
+test_pop.load_grid_function()
 
 
-# test_pop.run_grid()
-
-test_pop.load_grid_function()
\ No newline at end of file
+print(test_pop.grid_options['code_string'])
\ No newline at end of file
-- 
GitLab