0001: #!/usr/bin/perl -w
0002: #!/usr/bin/perl -d:ptkdb -w
0003: #
0004: 
0005: use strict;
0006: 
0007: 
0008: $| = 1;
0009: 
0010: 
0011: BEGIN
0012: {
0013:     #! make check
0014: 
0015:     push @INC, '../perl';
0016: 
0017:     #! make distcheck
0018: 
0019:     push @INC, '../../perl';
0020: 
0021:     #! normal run
0022: 
0023:     push @INC, './perl';
0024: 
0025:     #! after install
0026: 
0027:     push @INC, '/usr/local/glue/swig/perl';
0028: 
0029:     # check for ".genesis3" directory. If not present we create
0030:     # it and set a directory for perl inline code. 
0031:     my $inline_path = "$ENV{HOME}/.genesis3/ssp/InlineCode";
0032:     if(! -e $inline_path)
0033:     {
0034:       use File::Path;
0035: 
0036:       &File::Path::mkpath($inline_path);
0037:     }
0038: 
0039:     $ENV{PERL_INLINE_DIRECTORY} = $inline_path;
0040: 
0041: }
0042: 
0043: 
0044: use Getopt::Long;
0045: 
0046: use SSP;
0047: 
0048: use YAML;
0049: 
0050: 
0051: $SIG{__DIE__}
0052:     = sub {
0053:         use Carp;
0054: 
0055:         confess @_;
0056:     };
0057: 
0058: 
0059: my $option_configuration_cell;
0060: my $option_daemonize;
0061: my $option_debug;
0062: my $option_dump;
0063: my $option_dump_extended;
0064: my $option_emit_output = [];
0065: my $option_emit_schedules;
0066: my $option_history;
0067: my $option_inject_delay;
0068: my $option_inject_duration;
0069: my $option_inject_magnitude;
0070: my $option_model_directory;
0071: my $option_model_filename;
0072: my $option_model_name;
0073: my $option_neurospaces_models = '/usr/local/neurospaces/models/library';
0074: my $option_neurospaces_studio;
0075: my $option_optimize;
0076: my $option_output_fields = [];
0077: my $option_parameters = [];
0078: my $option_perfectclamp; 
0079: my $option_pulsegen_level1;
0080: my $option_pulsegen_width1;
0081: my $option_pulsegen_delay1;
0082: my $option_pulsegen_level2;
0083: my $option_pulsegen_width2;
0084: my $option_pulsegen_delay2;
0085: my $option_pulsegen_baselevel;
0086: my $option_pulsegen_triggermode;
0087: my $option_set_name;
0088: my $option_set_outputclass_filename;
0089: my $option_solverclass = 'heccer';
0090: my $option_spine_prototype;
0091: my $option_steps;
0092: my $option_time = 0.050;
0093: my $option_time_step;
0094: my $option_transformator = "";
0095: our $option_verbose;
0096: 
0097: my $exit_code = 0;
0098: 
0099: 
0100: my $builtin_configurations
0101:     = {
0102:        cell => {
0103:                 apply => {
0104:                           simulation => [
0105:                                          {
0106:                                           arguments => [ 0, { verbose => $option_verbose, }, ],
0107:                                           method => 'steps',
0108:                                          },
0109:                                         ],
0110:                          },
0111:                 usage => '
0112:         Simulate a single model neuron, default is to output the membrane potential of the soma.
0113:         Use the options to inject current in the soma (--inject-magnitude), or alternatively
0114:         to set a command voltage (--perfectclamp).
0115:         The model\'s soma segment must reside in a SEGMENT_GROUP with name "segments".
0116: 
0117:         The name of the model neuron is inferred from the name of the model description file.
0118:         (e.g. a model description file called "hh_neuron.ndf" is assumed to define a model neuron
0119:         called "hh_neuron").
0120: 
0121:         --model-name overwrite the default model name.
0122:         --steps sets number of steps
0123: ',
0124:                },
0125:       };
0126: 
0127: 
0128: sub apply_settings
0129: {
0130:     my $scheduler = shift;
0131: 
0132:     my $settings = shift;
0133: 
0134:     # loop over all services
0135: 
0136:     my $services = $scheduler->{services};
0137: 
0138:     foreach my $service_name (keys %$services)
0139:     {
0140:         my $service = $services->{$service_name};
0141: 
0142:         # if this is the neurospaces service
0143: 
0144:         if ($service->{module_name} eq 'Neurospaces')
0145:         {
0146:             # if the library option is set
0147: 
0148:             if (defined $option_neurospaces_models)
0149:             {
0150:                 # set the option for the service
0151: 
0152:                 $service->{model_library} = $option_neurospaces_models;
0153:             }
0154:         }
0155:     }
0156: }
0157: 
0158: 
0159: sub execute
0160: {
0161:     my $filename = shift;
0162: 
0163:     my $options = shift;
0164: 
0165:     # if the configuration exists
0166: 
0167:     if (-e $filename)
0168:     {
0169:         # construct schedule from the configuration
0170: 
0171:         my $scheduler;
0172: 
0173:         eval
0174:         {
0175:             local $/;
0176: 
0177:             $scheduler = Load(`cat "$filename"`);
0178:         };
0179: 
0180:         if ($@)
0181:         {
0182:             print "$0: scheduler cannot be constructed from '$filename': $@, ignoring this schedule\n";
0183: 
0184:             return;
0185:         }
0186: 
0187:         # fill in the name of the schedule
0188: 
0189:         if ($options->{inherit_name})
0190:         {
0191:             $scheduler->{name} = $filename;
0192:         }
0193: 
0194:         # fill in the name of the output class
0195: 
0196:         if ($options->{inherit_outputclass})
0197:         {
0198:             # loop over all output classes
0199: 
0200:             my $outputclasses = $scheduler->{outputclasses};
0201: 
0202:             foreach my $outputclass_name (keys %$outputclasses)
0203:             {
0204:                 # start with schedule filename
0205: 
0206:                 my $outputname = $filename;
0207: 
0208:                 # remove all directory names
0209: 
0210:                 $outputname =~ s(.*/)();
0211: 
0212:                 # remove possible .yml extension, gives output filename
0213: 
0214:                 $outputname =~ s(\.yml$)()i;
0215: 
0216:                 # we assume all output is directed to an output directory
0217: 
0218:                 $outputname = "./output/$outputname";
0219: 
0220:                 # fill in output class
0221: 
0222:                 $outputclasses->{$outputclass_name}->{options}->{filename} = $outputname;
0223:             }
0224:         }
0225: 
0226:         # and run it
0227: 
0228:         schedule($scheduler);
0229:     }
0230:     else
0231:     {
0232:         print STDERR "$0: configuration $filename cannot be found\n";
0233: 
0234:         $exit_code = 1;
0235:     }
0236: }
0237: 
0238: 
0239: sub main
0240: {
0241:     read_cmd_line();
0242: 
0243:     # loop over all configurations
0244: 
0245:     foreach my $configuration (@ARGV)
0246:     {
0247:         # execute the configuration
0248: 
0249:         execute
0250:             (
0251:              $configuration,
0252:              {
0253:               inherit_name => $option_set_name,
0254:               inherit_outputclass => $option_set_outputclass_filename,
0255:              },
0256:             );
0257:     }
0258: 
0259:     # if using one of the builtin configurations
0260: 
0261:     if ($option_configuration_cell)
0262:     {
0263: #       if (!defined $option_model_filename)
0264: #       {
0265: #           system "$0 --help";
0266: 
0267: #           die "option_model_name must be set when using the 'cell' builtin configuration without a filename";
0268: #       }
0269: 
0270: #       if (!defined $option_model_filename)
0271: #       {
0272: #           system "$0 --help";
0273: 
0274: #           die "option_model_filename must be set when using a builtin configuration";
0275: #       }
0276: 
0277:         if (!defined $option_model_filename)
0278:         {
0279:             $option_model_filename = $option_configuration_cell;
0280:         }
0281: 
0282:         if (!defined $option_model_name)
0283:         {
0284:             $option_model_filename =~ m((.*)\.(ndf|p|swc))i;
0285: 
0286:             $option_model_name = $1;
0287: 
0288:             $option_model_name =~ s(.*/)();
0289: 
0290:             if (!defined $option_model_name)
0291:             {
0292:                 system "$0 --help";
0293: 
0294:                 die "option_model_name cannot be determined from the option_model_filename due to a regex mismatch, bailing out";
0295:             }
0296: 
0297:             if ($option_verbose)
0298:             {
0299:                 print "Inferring model name from $option_model_filename, model name set to $option_model_name\nUse the --model-name option to overwrite\n";
0300:             }
0301:         }
0302: 
0303:         # construct a schedule from a builtin
0304: 
0305:         use Clone qw(clone);
0306: 
0307:         my $scheduler = clone($builtin_configurations->{cell});
0308: 
0309:         # assign name
0310: 
0311:         $scheduler->{name} = "builtin cell configuration, applied to: $option_model_name";
0312: 
0313:         # install dumper analyzers
0314: 
0315:         if ($option_dump)
0316:         {
0317:             use Heccer;
0318: 
0319:             my $extended_dump_selection
0320:                 = ($SwiggableHeccer::HECCER_DUMP_INDEXERS_SUMMARY
0321:                    + $SwiggableHeccer::HECCER_DUMP_INDEXERS_STRUCTURE
0322:                    + $SwiggableHeccer::HECCER_DUMP_INTERMEDIARY_COMPARTMENTS_PARAMETERS
0323:                    + $SwiggableHeccer::HECCER_DUMP_INTERMEDIARY_COMPARTMENT_SUMMARY
0324:                    + $SwiggableHeccer::HECCER_DUMP_INTERMEDIARY_MECHANISM_SUMMARY
0325:                    + $SwiggableHeccer::HECCER_DUMP_INTERMEDIARY_STRUCTURE
0326:                    + $SwiggableHeccer::HECCER_DUMP_INTERMEDIARY_SUMMARY
0327:                    + $SwiggableHeccer::HECCER_DUMP_TABLE_GATE_SUMMARY
0328:                    + $SwiggableHeccer::HECCER_DUMP_TABLE_GATE_TABLES
0329:                    + $SwiggableHeccer::HECCER_DUMP_VM_COMPARTMENT_MATRIX
0330:                    + $SwiggableHeccer::HECCER_DUMP_VM_COMPARTMENT_MATRIX_DIAGONALS
0331:                    + $SwiggableHeccer::HECCER_DUMP_VM_COMPARTMENT_OPERATIONS
0332:                    + $SwiggableHeccer::HECCER_DUMP_VM_MECHANISM_DATA
0333:                    + $SwiggableHeccer::HECCER_DUMP_VM_MECHANISM_OPERATIONS
0334:                    + $SwiggableHeccer::HECCER_DUMP_VM_CHANNEL_POOL_FLUXES
0335:                    + $SwiggableHeccer::HECCER_DUMP_VM_SUMMARY
0336:                    + $SwiggableHeccer::HECCER_DUMP_VM_AGGREGATORS);
0337: 
0338:             $scheduler->{analyzers}
0339:                 = {
0340:                    dumper => {
0341:                               module_name => 'Heccer',
0342:                               package => 'Heccer::Dumper',
0343:                               initializers => [
0344:                                                {
0345:                                                 arguments => [
0346:                                                               { source => "model_container::/$option_model_name", },
0347: 
0348:                                                               #! If I am correct, the next arguments are FILE* (undef for NULL), and dump selection
0349: 
0350:                                                               undef,
0351: 
0352:                                                               ($option_dump_extended
0353:                                                                ? $extended_dump_selection
0354:                                                                : undef),
0355:                                                              ],
0356:                                                 method => 'dump',
0357:                                                },
0358:                                               ],
0359:                              },
0360:                   };
0361:         }
0362: 
0363:         # process service options
0364: 
0365:         my $services
0366:             = {
0367:                model_container => {
0368:                                    module_name => 'Neurospaces',
0369:                                   },
0370:               };
0371: 
0372:         # if this is a neurospaces .ndf file
0373: 
0374:         if  ($option_model_filename =~ m(\.ndf$))
0375:         {
0376:             # use the 'read' method to initialize the model container
0377: 
0378:             my $args = [ "$0", "-P", ];
0379: 
0380:             push @$args, $option_model_filename;
0381: 
0382:             $services->{model_container}->{initializers}
0383:                 = [
0384:                    {
0385:                     arguments => [ $args, ],
0386:                     method => 'read',
0387:                    },
0388:                   ];
0389:         }
0390: 
0391:         # else a true morphology file
0392: 
0393:         else
0394:         {
0395:             # use the 'load' method to initializer the model container
0396: 
0397:             #! this will also convert .p and .swc files respecting the
0398:             #! conversion library.
0399: 
0400:             my $args = [ "$0", "-P", ];
0401: 
0402:             $services->{model_container}->{initializers}
0403:                 = [
0404:                    {
0405:                     arguments => [
0406:                                   {
0407:                                    filename => $option_model_filename,
0408:                                    spine_prototype => $option_spine_prototype,
0409:                                   },
0410:                                   $args,
0411:                                  ],
0412:                     method => 'load',
0413:                    },
0414:                   ];
0415: 
0416:         }
0417: 
0418:         $scheduler->{services} = $services;
0419: 
0420:         # infer the model name
0421: 
0422:         my $model_root = $option_model_name;
0423: 
0424:         # add an initial slash to the model name if necessary
0425: 
0426:         if ($model_root !~ m|^/|)
0427:         {
0428:             print "adding an initial slash to the model name $model_root\n";
0429: 
0430:             $model_root = "/$model_root";
0431:         }
0432: 
0433:         # link model name with solver class
0434: 
0435:         my $models
0436:             = [
0437:                {
0438:                 modelname => $model_root,
0439:                 solverclass => $option_solverclass,
0440:                },
0441:               ];
0442: 
0443:         # no conceptual parameters, has been obsoleted
0444: 
0445: #       my $conceptual_parameters
0446: #           = [
0447: #              {
0448: #               component_name => 'thickd::gaba::/Purk_GABA',
0449: #               description => 'endogenous activity',
0450: #               field => 'FREQUENCY',
0451: #               value => '1',
0452: #              },
0453: #              {
0454: #               component_name => 'spine::/Purk_spine/head/par',
0455: #               description => 'endogenous activity',
0456: #               field => 'FREQUENCY',
0457: #               value => '25',
0458: #              },
0459: #             ];
0460: 
0461: #       $models->[0]->{conceptual_parameters} = $conceptual_parameters;
0462: 
0463:         # fill in granular parameters for this model
0464: 
0465:         my $granular_parameters;
0466: 
0467:         if ($option_inject_magnitude
0468:             and !$option_inject_delay
0469:             and !$option_inject_duration)
0470:         {
0471:             $granular_parameters
0472:                 = [
0473:                    {
0474:                     component_name => "$model_root/segments/soma",
0475:                     field => 'INJECT',
0476:                     value => $option_inject_magnitude,
0477:                     warn_only => 'the component_name is a default, its address may be wrong',
0478:                    },
0479:                    {
0480:                     component_name => "$model_root/soma",
0481:                     field => 'INJECT',
0482:                     value => $option_inject_magnitude,
0483:                     warn_only => 'the component_name is a default, its address may be wrong',
0484:                    },
0485:                   ];
0486:         }
0487:         else
0488:         {
0489:             $granular_parameters
0490:                 = [
0491:                   ];
0492:         }
0493: 
0494:         $granular_parameters
0495:             = [
0496:                @$granular_parameters,
0497:                map
0498:                {
0499:                    my $setting = $_;
0500: 
0501:                    $setting =~ m/^(.*)->(.*)=(.*)$/;
0502: 
0503:                    my $component_name = $1;
0504: 
0505:                    my $field = $2;
0506: 
0507:                    my $value = $3;
0508: 
0509:                    my $result
0510:                        = {
0511:                           component_name => $component_name,
0512:                           field => $field,
0513:                           value => $value,
0514:                          };
0515: 
0516:                    $result;
0517:                }
0518:                @$option_parameters,
0519:               ];
0520: 
0521:         $models->[0]->{granular_parameters} = $granular_parameters;
0522: 
0523:         # fill in the model section of the schedule
0524: 
0525:         $scheduler->{models} = $models;
0526: 
0527:         # define the solver class
0528: 
0529:         my $option_solverclass_name = 'Heccer';
0530: 
0531:         my $solverclasses
0532:             = {
0533:                heccer => {
0534: #                         constructor_settings => {
0535: #                                                  dStep => (2e-5),
0536: #                                                  configuration => {
0537: #                                                                    reporting => {
0538: #                                                                                  granularity => 1000,
0539: #                                                                                  tested_things => (
0540: #                                                                                                    $SwiggableHeccer::HECCER_DUMP_VM_COMPARTMENT_MATRIX
0541: #                                                                                                    | $SwiggableHeccer::HECCER_DUMP_VM_COMPARTMENT_DATA
0542: #                                                                                                    | $SwiggableHeccer::HECCER_DUMP_VM_COMPARTMENT_OPERATIONS
0543: #                                                                                                    | $SwiggableHeccer::HECCER_DUMP_VM_MECHANISM_DATA
0544: #                                                                                                    | $SwiggableHeccer::HECCER_DUMP_VM_MECHANISM_OPERATIONS
0545: #                                                                                                    | $SwiggableHeccer::HECCER_DUMP_VM_SUMMARY
0546: #                                                                                                   ),
0547: #                                                                                 },
0548: #                                                                   },
0549: #                                                 },
0550:                           module_name => $option_solverclass_name,
0551:                           service_name => 'model_container',
0552:                          },
0553:               };
0554: 
0555:         if (defined $option_time_step)
0556:         {
0557:             $solverclasses->{heccer}->{constructor_settings}->{dStep} = $option_time_step;
0558:         }
0559: 
0560:         # fill in solver class in the schedule
0561: 
0562:         $scheduler->{solverclasses} = $solverclasses;
0563: 
0564:         # define inputclasses and inputs according to the options
0565: 
0566:         if (defined $option_perfectclamp)
0567:         {
0568:             my $perfectclamp_inputclassname = "perfectclamp";
0569: 
0570:             my $inputclass
0571:                 = {
0572:                    module_name => "Heccer",
0573:                    options => {
0574:                                command => $option_perfectclamp,
0575:                                name => "perfectclamp set to $option_perfectclamp",
0576:                               },
0577:                    package => "Heccer::PerfectClamp",
0578:                   };
0579: 
0580:             my $inputs
0581:                 = [
0582:                    {
0583:                     component_name => "$model_root/segments/soma",
0584:                     field => "Vm",
0585:                     inputclass => $perfectclamp_inputclassname,
0586:                    },
0587:                   ];
0588: 
0589:             $scheduler->{inputclasses}->{$perfectclamp_inputclassname} = $inputclass;
0590: 
0591:             if (!defined $scheduler->{inputs})
0592:             {
0593:                 $scheduler->{inputs} = [];
0594:             }
0595: 
0596:             push @{$scheduler->{inputs}}, @$inputs;
0597:         }
0598: 
0599:         if (defined $option_pulsegen_width1)
0600:         {
0601:             my $pulsegen_inputclassname = "pulsegen";
0602: 
0603:             my $inputclass
0604:                 = {
0605:                    module_name => "Heccer",
0606:                    options => {
0607:                                command => $option_pulsegen_width1,
0608:                                name => "pulsegen set to $option_pulsegen_width1",
0609:                               },
0610:                    package => "Heccer::Pulsegen",
0611:                   };
0612: 
0613:             my $inputs
0614:                 = [
0615:                    {
0616:                     component_name => "$model_root/segments/soma",
0617:                     field => "Vm",
0618:                     inputclass => $pulsegen_inputclassname,
0619:                    },
0620:                   ];
0621: 
0622:             $scheduler->{inputclasses}->{$pulsegen_inputclassname} = $inputclass;
0623: 
0624:             if (!defined $scheduler->{inputs})
0625:             {
0626:                 $scheduler->{inputs} = [];
0627:             }
0628: 
0629:             push @{$scheduler->{inputs}}, @$inputs;
0630:         }
0631: 
0632: 
0633:         if (defined $option_pulsegen_level1)
0634:         {
0635:             my $pulsegen_inputclassname = "pulsegen";
0636: 
0637:             my $inputclass
0638:                 = {
0639:                    module_name => "Heccer",
0640:                    options => {
0641:                                command => $option_pulsegen_level1,
0642:                                name => "pulsegen set to $option_pulsegen_level1",
0643:                               },
0644:                    package => "Heccer::Pulsegen",
0645:                   };
0646: 
0647:             my $inputs
0648:                 = [
0649:                    {
0650:                     component_name => "$model_root/segments/soma",
0651:                     field => "Vm",
0652:                     inputclass => $pulsegen_inputclassname,
0653:                    },
0654:                   ];
0655: 
0656:             $scheduler->{inputclasses}->{$pulsegen_inputclassname} = $inputclass;
0657: 
0658:             if (!defined $scheduler->{inputs})
0659:             {
0660:                 $scheduler->{inputs} = [];
0661:             }
0662: 
0663:             push @{$scheduler->{inputs}}, @$inputs;
0664:         }
0665: 
0666:         if (defined $option_pulsegen_delay1)
0667:         {
0668:             my $pulsegen_inputclassname = "pulsegen";
0669: 
0670:             my $inputclass
0671:                 = {
0672:                    module_name => "Heccer",
0673:                    options => {
0674:                                command => $option_pulsegen_delay1,
0675:                                name => "pulsegen set to $option_pulsegen_delay1",
0676:                               },
0677:                    package => "Heccer::Pulsegen",
0678:                   };
0679: 
0680:             my $inputs
0681:                 = [
0682:                    {
0683:                     component_name => "$model_root/segments/soma",
0684:                     field => "Vm",
0685:                     inputclass => $pulsegen_inputclassname,
0686:                    },
0687:                   ];
0688: 
0689:             $scheduler->{inputclasses}->{$pulsegen_inputclassname} = $inputclass;
0690: 
0691:             if (!defined $scheduler->{inputs})
0692:             {
0693:                 $scheduler->{inputs} = [];
0694:             }
0695: 
0696:             push @{$scheduler->{inputs}}, @$inputs;
0697:         }
0698: 
0699:         if (defined $option_pulsegen_width2)
0700:         {
0701:             my $pulsegen_inputclassname = "pulsegen";
0702: 
0703:             my $inputclass
0704:                 = {
0705:                    module_name => "Heccer",
0706:                    options => {
0707:                                command => $option_pulsegen_width2,
0708:                                name => "pulsegen set to $option_pulsegen_width2",
0709:                               },
0710:                    package => "Heccer::Pulsegen",
0711:                   };
0712: 
0713:             my $inputs
0714:                 = [
0715:                    {
0716:                     component_name => "$model_root/segments/soma",
0717:                     field => "Vm",
0718:                     inputclass => $pulsegen_inputclassname,
0719:                    },
0720:                   ];
0721: 
0722:             $scheduler->{inputclasses}->{$pulsegen_inputclassname} = $inputclass;
0723: 
0724:             if (!defined $scheduler->{inputs})
0725:             {
0726:                 $scheduler->{inputs} = [];
0727:             }
0728: 
0729:             push @{$scheduler->{inputs}}, @$inputs;
0730:         }
0731: 
0732: 
0733:         if (defined $option_pulsegen_level2)
0734:         {
0735:             my $pulsegen_inputclassname = "pulsegen";
0736: 
0737:             my $inputclass
0738:                 = {
0739:                    module_name => "Heccer",
0740:                    options => {
0741:                                command => $option_pulsegen_level2,
0742:                                name => "pulsegen set to $option_pulsegen_level2",
0743:                               },
0744:                    package => "Heccer::Pulsegen",
0745:                   };
0746: 
0747:             my $inputs
0748:                 = [
0749:                    {
0750:                     component_name => "$model_root/segments/soma",
0751:                     field => "Vm",
0752:                     inputclass => $pulsegen_inputclassname,
0753:                    },
0754:                   ];
0755: 
0756:             $scheduler->{inputclasses}->{$pulsegen_inputclassname} = $inputclass;
0757: 
0758:             if (!defined $scheduler->{inputs})
0759:             {
0760:                 $scheduler->{inputs} = [];
0761:             }
0762: 
0763:             push @{$scheduler->{inputs}}, @$inputs;
0764:         }
0765: 
0766:         if (defined $option_pulsegen_delay2)
0767:         {
0768:             my $pulsegen_inputclassname = "pulsegen";
0769: 
0770:             my $inputclass
0771:                 = {
0772:                    module_name => "Heccer",
0773:                    options => {
0774:                                command => $option_pulsegen_delay2,
0775:                                name => "pulsegen set to $option_pulsegen_delay2",
0776:                               },
0777:                    package => "Heccer::Pulsegen",
0778:                   };
0779: 
0780:             my $inputs
0781:                 = [
0782:                    {
0783:                     component_name => "$model_root/segments/soma",
0784:                     field => "Vm",
0785:                     inputclass => $pulsegen_inputclassname,
0786:                    },
0787:                   ];
0788: 
0789:             $scheduler->{inputclasses}->{$pulsegen_inputclassname} = $inputclass;
0790: 
0791:             if (!defined $scheduler->{inputs})
0792:             {
0793:                 $scheduler->{inputs} = [];
0794:             }
0795: 
0796:             push @{$scheduler->{inputs}}, @$inputs;
0797:         }
0798: 
0799: 
0800:         if (defined $option_pulsegen_baselevel)
0801:         {
0802:             my $pulsegen_inputclassname = "pulsegen";
0803: 
0804:             my $inputclass
0805:                 = {
0806:                    module_name => "Heccer",
0807:                    options => {
0808:                                command => $option_pulsegen_baselevel,
0809:                                name => "pulsegen set to $option_pulsegen_baselevel",
0810:                               },
0811:                    package => "Heccer::Pulsegen",
0812:                   };
0813: 
0814:             my $inputs
0815:                 = [
0816:                    {
0817:                     component_name => "$model_root/segments/soma",
0818:                     field => "Vm",
0819:                     inputclass => $pulsegen_inputclassname,
0820:                    },
0821:                   ];
0822: 
0823:             $scheduler->{inputclasses}->{$pulsegen_inputclassname} = $inputclass;
0824: 
0825:             if (!defined $scheduler->{inputs})
0826:             {
0827:                 $scheduler->{inputs} = [];
0828:             }
0829: 
0830:             push @{$scheduler->{inputs}}, @$inputs;
0831:         }
0832: 
0833: 
0834:         if (defined $option_pulsegen_triggermode)
0835:         {
0836:             my $pulsegen_inputclassname = "pulsegen";
0837: 
0838:             my $inputclass
0839:                 = {
0840:                    module_name => "Heccer",
0841:                    options => {
0842:                                command => $option_pulsegen_triggermode,
0843:                                name => "pulsegen set to $option_pulsegen_triggermode",
0844:                               },
0845:                    package => "Heccer::Pulsegen",
0846:                   };
0847: 
0848:             my $inputs
0849:                 = [
0850:                    {
0851:                     component_name => "$model_root/segments/soma",
0852:                     field => "Vm",
0853:                     inputclass => $pulsegen_inputclassname,
0854:                    },
0855:                   ];
0856: 
0857:             $scheduler->{inputclasses}->{$pulsegen_inputclassname} = $inputclass;
0858: 
0859:             if (!defined $scheduler->{inputs})
0860:             {
0861:                 $scheduler->{inputs} = [];
0862:             }
0863: 
0864:             push @{$scheduler->{inputs}}, @$inputs;
0865:         }
0866: 
0867: 
0868:         # define an output filename
0869: 
0870:         my $option_output_directory = './output';
0871: 
0872:         my $option_output_filename = $option_output_directory . $model_root . ".out";
0873: 
0874:         # create the directory for the output file
0875: 
0876:         $option_output_filename =~ m((.*)/);
0877: 
0878:         my $directory = $1;
0879: 
0880:         if ($1)
0881:         {
0882:             `mkdir -p "$1"`;
0883:         }
0884: 
0885:         # we assume the solver exports a suitable output class
0886: 
0887:         #! not sure if this is a good idea
0888: 
0889:         my $option_output_package = $option_solverclass_name . '::' . 'Output';
0890: 
0891:         # assemble output class definition
0892: 
0893:         my $outputclasses
0894:             = {
0895:                double_2_ascii => {
0896:                                   module_name => $option_solverclass_name,
0897:                                   options => {
0898:                                               filename => $option_output_filename,
0899:                                              },
0900:                                   package => $option_output_package,
0901:                                  },
0902:               };
0903: 
0904:         # fill in the output class in the schedule
0905: 
0906:         $scheduler->{outputclasses} = $outputclasses;
0907: 
0908:         # link the model outputs with the output class
0909: 
0910:         if (!@$option_output_fields)
0911:         {
0912:             $option_output_fields = [ "$model_root/segments/soma->Vm", ];
0913:         }
0914: 
0915:         my $outputs
0916:             = [
0917:                map
0918:                {
0919:                    my $address = $_;
0920: 
0921:                    if ($address !~ m/^(.*)->(.*)$/)
0922:                    {
0923:                        die "$0: unknown output specification $address.  Output specifications must be in the format 'component_name'->'field_name' (eg. --output '/Purkinje/segments/soma->Vm')";
0924:                    }
0925: 
0926:                    my $component_name = $1;
0927: 
0928:                    my $field = $2;
0929: 
0930:                    if ($component_name !~ /^\//)
0931:                    {
0932:                        $component_name = "/$component_name";
0933:                    }
0934: 
0935:                    my $result
0936:                        = {
0937:                           component_name => $component_name,
0938:                           field => $field,
0939:                           outputclass => 'double_2_ascii',
0940:                          };
0941: 
0942:                    $result;
0943:                }
0944:                @$option_output_fields,
0945:               ];
0946: 
0947:         $scheduler->{outputs} = $outputs;
0948: 
0949:         # set the apply options
0950: 
0951:         # set verbosity level
0952: 
0953:         my $apply
0954:             = {
0955:                simulation => [
0956:                               {
0957:                                arguments => [ 0, { verbose => 0, }, ],
0958:                                method => 'steps',
0959:                               },
0960:                              ],
0961:               };
0962: 
0963:         my $apply_time
0964:             = {
0965:                simulation => [
0966:                               {
0967:                                arguments => [ 0, { verbose => 0, }, ],
0968:                                method => 'advance',
0969:                               },
0970:                              ],
0971:               };
0972: 
0973:         if (defined $option_verbose)
0974:         {
0975:             $apply->{simulation}->[0]->{arguments}->[1]->{verbose} = $option_verbose;
0976:         }
0977: 
0978:         # setting the number of steps cannot be combined with inject duration and delay
0979: 
0980:         if (defined $option_steps
0981:             and (defined $option_inject_delay or defined $option_inject_duration))
0982:         {
0983:             die "$0: option inject-delay and option inject-duration cannot be combined with option steps";
0984:         }
0985: 
0986:         # set number of steps
0987: 
0988:         if (defined $option_steps)
0989:         {
0990:             $apply->{simulation}->[0]->{arguments}->[0] = $option_steps;
0991:         }
0992: 
0993:         # set simulation time
0994: 
0995:         if (defined $option_time)
0996:         {
0997:             if (not defined $option_inject_delay and not defined $option_inject_duration)
0998:             {
0999:                 $apply_time->{simulation}->[0]->{arguments}->[0] = $option_time;
1000:             }
1001:             else
1002:             {
1003:                 if (!defined $option_inject_delay)
1004:                 {
1005:                     $option_inject_delay = 0;
1006:                 }
1007: 
1008:                 my $time_termination = $option_time - $option_inject_delay - $option_inject_duration;
1009: 
1010:                 $apply_time
1011:                     = {
1012:                        simulation => [
1013:                                       {
1014:                                        arguments => [ $option_inject_delay, { verbose => 0, }, ],
1015:                                        method => 'advance',
1016:                                       },
1017:                                       {
1018:                                        arguments => [
1019:                                                      {
1020:                                                       component_name => "$model_root/segments/soma",
1021:                                                       field => 'INJECT',
1022:                                                       modelname => "$model_root",
1023:                                                       value => $option_inject_magnitude,
1024:                                                      },
1025:                                                     ],
1026:                                        method => 'apply_runtime_parameters',
1027:                                       },
1028:                                       {
1029:                                        arguments => [ $option_inject_duration, { verbose => 0, }, ],
1030:                                        method => 'advance',
1031:                                       },
1032:                                       {
1033:                                        arguments => [
1034:                                                      {
1035:                                                       component_name => "$model_root/segments/soma",
1036:                                                       field => 'INJECT',
1037:                                                       modelname => "$model_root",
1038:                                                       value => 0,
1039:                                                      },
1040:                                                     ],
1041:                                        method => 'apply_runtime_parameters',
1042:                                       },
1043:                                       {
1044:                                        arguments => [ $time_termination, { verbose => 0, }, ],
1045:                                        method => 'advance',
1046:                                       },
1047:                                      ],
1048:                       };
1049:             }
1050:         }
1051: 
1052:         defined $option_steps
1053:             ? $scheduler->{apply} = $apply
1054:                 : $scheduler->{apply} = $apply_time;
1055: 
1056:         # dump the schedule
1057: 
1058:         if ($option_verbose)
1059:         {
1060:             print Dump("Running schedule") . Dump($scheduler);
1061:         }
1062: 
1063:         # and run it
1064: 
1065:         schedule($scheduler);
1066:     }
1067: 
1068:     # now emit all the output files
1069: 
1070:     if ($option_verbose)
1071:     {
1072:         print "\noutput files follow\n---\n";
1073:     }
1074: 
1075:     foreach my $emit_output (@$option_emit_output)
1076:     {
1077:         system "cat", $emit_output;
1078:     }
1079: 
1080: }
1081: 
1082: 
1083: sub read_cmd_line
1084: {
1085:     my $option_builtins;
1086:     my $option_help;
1087:     my $option_version;
1088: 
1089:     my $result
1090:         = GetOptions
1091:             (
1092:              "builtins!" => \$option_builtins,
1093:              "cell=s" => \$option_configuration_cell,
1094:              "daemonize!" => \$option_daemonize,
1095:              "debug=s" => \$option_debug,
1096:              "dump!" => \$option_dump,
1097:              "dump-extended!", => \$option_dump_extended,
1098:              "emit-output=s" => $option_emit_output,
1099:              "emit-schedules!" => \$option_emit_schedules,
1100:              "help!" => \$option_help,
1101:              "history!" => \$option_history,
1102:              "inject-delay=s" => \$option_inject_delay,
1103:              "inject-duration=s" => \$option_inject_duration,
1104:              "inject-magnitude=s" => \$option_inject_magnitude,
1105:              "model-directory=s" => \$option_model_directory,
1106:              "model-filename=s" => \$option_model_filename,
1107:              "model-name=s" => \$option_model_name,
1108:              "neurospaces-models=s" => \$option_neurospaces_models,
1109:              "neurospaces-studio" => \$option_neurospaces_studio,
1110:              "optimize!" => \$option_optimize,
1111:              "output-fields=s" => $option_output_fields,
1112:              "parameters=s" => $option_parameters,
1113:              "perfectclamp=s" => \$option_perfectclamp,
1114:              "pulsegen-level1=s" => \$option_pulsegen_level1,
1115:              "pulsegen-width1=s" => \$option_pulsegen_width1,
1116:              "pulsegen-delay1=s" => \$option_pulsegen_delay1,
1117:              "pulsegen-level2=s" => \$option_pulsegen_level2,
1118:              "pulsegen-width2=s" => \$option_pulsegen_width2,
1119:              "pulsegen-delay2=s" => \$option_pulsegen_delay2,
1120:              "pulsegen-baselevel=s" => \$option_pulsegen_baselevel,
1121:              "pulsegen-triggermode=s" => \$option_pulsegen_triggermode,
1122:              "set-name=s" => \$option_set_name,
1123:              "set-outputclass-filename=s" => \$option_set_outputclass_filename,
1124:              "solverclass=s" => \$option_solverclass,
1125:              "spine-prototype=s" => \$option_spine_prototype,
1126:              "steps=i" => \$option_steps,
1127:              "time=s" => \$option_time,
1128:              "time-step=s" => \$option_time_step,
1129:              "transformator=s" => \$option_transformator,
1130:              "v|verbose+" => \$option_verbose,
1131:              "version" => \$option_version,
1132:             );
1133: 
1134:     if ($option_version)
1135:     {
1136:         my $version = SSP::version();
1137: 
1138:         print $version . "\n";
1139: 
1140:         exit 1;
1141:     }
1142: 
1143:     if ($option_help
1144:         || !@ARGV
1145:         && !$option_builtins
1146:         && !$option_configuration_cell)
1147:     {
1148:         print
1149:             "
1150: $0 <configuration>
1151: 
1152: $0: run simulations, given an ssp configuration, or using a builtin configuration
1153: 
1154: try $0 --builtins for information of how to run a simulation easily
1155: 
1156: options:
1157:     --builtins           give help about supported builtin configurations.
1158:     --cell               use the cell builtin configuration.
1159:     --daemonize          detach from terminal, close shared resources and run in the background,
1160:                          note that this option currently inhibits any feedback of the process,
1161:                          so be cautious with it.
1162:     --debug              set to a string of the debugging package, 1 for a default of SSP::Debug.
1163:     --dump               install dumpers for analyzers.
1164:     --dump-extended      dump more, also dumps discretized tables ao.
1165:     --emit-output        files to write to stdout after the simulation finishes.
1166:     --emit-schedules     print schedules to stdout instead of running them.
1167:     --help               print usage information.
1168:     --history            emit a record of the history of the simulation after it finishes.
1169:     --inject-delay       delay of the current injection protocol.
1170:     --inject-duration    duration of the current injection protocol.
1171:     --inject-magnitude   amount of current injected into the soma.
1172:     --model-directory    name of the directory where to look for non-std models.
1173:     --model-filename     filename of the model description file (when using a builtin configuration).
1174:     --model-name         name of the model (when using a builtin configuration).
1175:     --neurospaces-models directory where to find the neurospaces library.
1176:     --neurospaces-studio replace the simulation routine with calls to the neurospaces studio,
1177:                          this allows to explore the model after all modifiers have completed.
1178:     --optimize           turn on the schedule optimizer.
1179:     --output-fields      define an output, can be given multiple times.
1180:     --parameters         set a specific parameter value, can be given multiple times.
1181:     --perfectclamp       set the command voltage for the perfect clamp protocol.
1182:     --pulsegen-width1    set the pulse width for the pulsegen protocol.
1183:     --pulsegen-level1    set the pulse level for the pulsegen protocol.
1184:     --pulsegen-delay1    set the pulse delay for the pulsegen protocol.
1185:     --pulsegen-width2    set the pulse width for the pulsegen protocol.
1186:     --pulsegen-level2    set the pulse level for the pulsegen protocol.
1187:     --pulsegen-delay2    set the pulse delay for the pulsegen protocol.
1188:     --pulsegen-baselevel set the pulse base level for the pulsegen protocol.
1189:     --pulsegen-triggermode  set the triggermode for the pulsegen protocol.
1190:     --set-name           overwrite the schedule name with the name of the file that contains the schedule.
1191:     --set-outputclass-filename
1192:                          overwrite the outputclass filename with something derived from the name of the
1193:                          file that contains the schedule.
1194:     --solverclass        set the solver class to use (when using a builtin configuration).
1195:     --spine-prototype    add spines with this prototype.
1196:     --steps              number of simulation steps, when using one of the builtin configurations.
1197:     --time               set simulation time (in seconds).
1198:     --time-step          sets the time step, when using one of the builtin configurations.
1199:     --transformator      feed the configuration through this transformator before running.
1200:     --verbose            set verbosity level.
1201:     --version            give version information.
1202: ";
1203: 
1204:         exit 1;
1205:     }
1206: 
1207:     if ($option_builtins)
1208:     {
1209:         print
1210:             "
1211: $0 <configuration>
1212: 
1213: $0: run simulations, given an ssp configuration, or using a builtin configuration
1214: 
1215: Known builtin configurations:
1216: 
1217: ";
1218: 
1219:         foreach my $builtin_configuration_name (keys %$builtin_configurations)
1220:         {
1221:             print "\n$builtin_configuration_name:\n\n";
1222: 
1223:             print $builtin_configurations->{$builtin_configuration_name}->{usage};
1224: 
1225:         }
1226: 
1227:         print "\n";
1228: 
1229:         exit 1;
1230:     }
1231: 
1232: }
1233: 
1234: 
1235: sub schedule
1236: {
1237:     my $scheduler = shift;
1238: 
1239:     {
1240:         # if option to replace the simulation routine with calls to the neurospaces studio
1241: 
1242:         if ($option_neurospaces_studio)
1243:         {
1244:             # load things related to the neurospaces studio
1245: 
1246:             #! for proper error reporting of loading modules (Renderer etc),
1247:             #! 'require' must be used, not 'use'.
1248: 
1249:             require Neurospaces;
1250:             require Neurospaces::Traversal;
1251:             require Neurospaces::Studio;
1252: 
1253:             #! this comes later in the neurospaces script, no clue why
1254: 
1255:             require Neurospaces::GUI;
1256: 
1257:             # replace the simulation routine
1258: 
1259:             $scheduler->{apply}->{simulation}
1260:                 = [
1261:                    {
1262:                     arguments => [ $0, ],
1263:                     method => 'Neurospaces::GUI::gui',
1264:                    },
1265:                   ];
1266: 
1267:             # remove the compilation and initiation parts, to prevent
1268:             # that output files get overwritten
1269: 
1270:             $scheduler->{apply}->{initializers} = [ undef, ];
1271:         }
1272: 
1273:         # if option to daemonize
1274: 
1275:         if ($option_daemonize)
1276:         {
1277:             # we tell to add the daemonizing call to the current initializers
1278: 
1279:             $scheduler->{apply}->{initializers}
1280:                 = {
1281:                    push => [
1282:                             {
1283:                              #! arguments: close_files, options hash
1284: 
1285:                              #! only STDIN, STDOUT, STDERR, because I think some of the GUI
1286:                              #! things have potentially already opened filehandles, or so.
1287:                              #! simply closing all filehandles inhibits the GUI when used
1288:                              #! invocated from the webserver.
1289: 
1290:                              arguments => [ 2, ],
1291:                              method => 'daemonize',
1292:                             },
1293:                            ],
1294:                    have => $scheduler->{apply}->{initializers},
1295:                    type => 'push',
1296:                   };
1297:         }
1298:     }
1299: 
1300:     # turn on ssp optimization if requested
1301: 
1302:     if ($option_optimize)
1303:     {
1304:         $scheduler->{optimize} = $option_optimize;
1305:     }
1306: 
1307:     # determine the package name of the scheduler
1308: 
1309:     my $package_name = "SSP";
1310: 
1311:     if ($option_debug)
1312:     {
1313:         $package_name = $option_debug;
1314: 
1315:         if ($package_name eq 1)
1316:         {
1317:             $package_name = "SSP::Debug";
1318:         }
1319:     }
1320: 
1321:     # now check if we indeed have a valid scheduler from that package name
1322: 
1323:     use UNIVERSAL qw( isa can );
1324: 
1325:     if (!isa($scheduler, $package_name))
1326:     {
1327:         # no, construct one
1328: 
1329:         $scheduler = $package_name->new( { %$scheduler, verbose => $option_verbose, }, );
1330:     }
1331: 
1332:     # apply the local settings
1333: 
1334:     my $settings
1335:         = {
1336:            neurospaces_models => $option_neurospaces_models,
1337:           };
1338: 
1339:     apply_settings($scheduler, $settings);
1340: 
1341:     # serialize the schedule
1342: 
1343:     my $serialized_schedule = Dump($scheduler);
1344: 
1345:     use IO::File;
1346: 
1347:     my $fh = IO::File->new(">/tmp/current_schedule");
1348: 
1349:     if ($fh)
1350:     {
1351:         print $fh $serialized_schedule;
1352: 
1353:         $fh->close();
1354:     }
1355: 
1356:     # if options say to emit the schedule
1357: 
1358:     if ($option_emit_schedules)
1359:     {
1360:         # emit the schedule
1361: 
1362:         print Dump($scheduler);
1363:     }
1364: 
1365:     # else
1366: 
1367:     else
1368:     {
1369:         # run the schedule
1370: 
1371:         if ($option_verbose)
1372:         {
1373:             print "$0: Running schedule $scheduler->{name}\n";
1374:         }
1375: 
1376:         $scheduler->run();
1377:     }
1378: 
1379:     # if history turned on
1380: 
1381:     if ($option_history)
1382:     {
1383:         my $history = $scheduler->history( { time_stamps => 0, }, );
1384: 
1385:         use YAML;
1386: 
1387:         print Dump( { history => $history, }, );
1388:     }
1389: }
1390: 
1391: 
1392: main();
1393: 
1394: 
1395: exit $exit_code;
1396: 
1397: 
1398: