ngcsimlib 0.2b0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ngcsimlib/__init__.py +50 -0
- ngcsimlib/bundle_rules.py +64 -0
- ngcsimlib/commands/__init__.py +11 -0
- ngcsimlib/commands/advanceState.py +25 -0
- ngcsimlib/commands/clamp.py +58 -0
- ngcsimlib/commands/command.py +34 -0
- ngcsimlib/commands/compound.py +39 -0
- ngcsimlib/commands/evolve.py +41 -0
- ngcsimlib/commands/multiclamp.py +42 -0
- ngcsimlib/commands/reset.py +40 -0
- ngcsimlib/commands/save.py +46 -0
- ngcsimlib/commands/snapshot.py +39 -0
- ngcsimlib/commands/track.py +50 -0
- ngcsimlib/component.py +359 -0
- ngcsimlib/controller.py +378 -0
- ngcsimlib/utils.py +237 -0
- ngcsimlib-0.2b0.dist-info/AUTHORS +10 -0
- ngcsimlib-0.2b0.dist-info/LICENSE +29 -0
- ngcsimlib-0.2b0.dist-info/METADATA +69 -0
- ngcsimlib-0.2b0.dist-info/RECORD +22 -0
- ngcsimlib-0.2b0.dist-info/WHEEL +5 -0
- ngcsimlib-0.2b0.dist-info/top_level.txt +1 -0
ngcsimlib/__init__.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from . import utils
|
|
2
|
+
from . import controller
|
|
3
|
+
from . import commands
|
|
4
|
+
|
|
5
|
+
import argparse, os, warnings, json
|
|
6
|
+
from types import SimpleNamespace
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from sys import argv
|
|
9
|
+
from importlib import import_module
|
|
10
|
+
|
|
11
|
+
from pkg_resources import get_distribution
|
|
12
|
+
|
|
13
|
+
__version__ = get_distribution('ngcsimlib').version ## set software version
|
|
14
|
+
|
|
15
|
+
###### Preload Modules
|
|
16
|
+
def preload():
|
|
17
|
+
parser = argparse.ArgumentParser(description='Build and run a model using ngclearn')
|
|
18
|
+
parser.add_argument("--modules", type=str, help='location of modules.json file')
|
|
19
|
+
|
|
20
|
+
args = parser.parse_args()
|
|
21
|
+
try:
|
|
22
|
+
module_path = args.modules
|
|
23
|
+
except:
|
|
24
|
+
module_path = None
|
|
25
|
+
|
|
26
|
+
if module_path is None:
|
|
27
|
+
module_path = "json_files/modules.json"
|
|
28
|
+
|
|
29
|
+
if not os.path.isfile(module_path):
|
|
30
|
+
warnings.warn("Missing file to preload modules from. Attempted to locate file at \"" + str(module_path) + "\"" )
|
|
31
|
+
return
|
|
32
|
+
|
|
33
|
+
with open(module_path, 'r') as file:
|
|
34
|
+
modules = json.load(file, object_hook=lambda d: SimpleNamespace(**d))
|
|
35
|
+
|
|
36
|
+
for module in modules:
|
|
37
|
+
mod = import_module(module.absolute_path)
|
|
38
|
+
utils._Loaded_Modules[module.absolute_path] = mod
|
|
39
|
+
|
|
40
|
+
for attribute in module.attributes:
|
|
41
|
+
atr = getattr(mod, attribute.name)
|
|
42
|
+
utils._Loaded_Attributes[attribute.name] = atr
|
|
43
|
+
|
|
44
|
+
utils._Loaded_Attributes[".".join([module.absolute_path, attribute.name])] = atr
|
|
45
|
+
if hasattr(attribute, "keywords"):
|
|
46
|
+
for keyword in attribute.keywords:
|
|
47
|
+
utils._Loaded_Attributes[keyword] = atr
|
|
48
|
+
|
|
49
|
+
if not Path(argv[0]).name == "sphinx-build" or Path(argv[0]).name == "build.py":
|
|
50
|
+
preload()
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Contains all the built-in bundle rules as well as the default one for an unbundled
|
|
3
|
+
cable. Bundle(s) usage is that it is to be added to a component's bundle rules
|
|
4
|
+
(Note: the overwrite rule is the default rule).
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Template for bundle rules. The name of the bundle rule should be meaningful to what it does. All bundle rules will
|
|
8
|
+
be called with the same three inputs: component, value, and destination_compartment. These are not passed by keyword but
|
|
9
|
+
they will always be in the same order. The component is the component that the bundle has as a destination (target).
|
|
10
|
+
The value is the signal that goes into (or comes along to) the connected bundle. The destination_compartment is
|
|
11
|
+
the compartment that the bundle is connected to. Overall, the general usage of bundle rules is to modify the
|
|
12
|
+
behavior of an input to a compartment; generally, they should NOT modify any other aspect of the target
|
|
13
|
+
component aside from the destination compartment and, as a warning, should not try to reference compartments by
|
|
14
|
+
name as this will possibly result in runtime errors given that the required compartments for the bundle rule(s)
|
|
15
|
+
might not exist.
|
|
16
|
+
|
|
17
|
+
General bundle rule specification:
|
|
18
|
+
|
|
19
|
+
def BUNDLE_RULE_NAME(component, value, destination_compartment):
|
|
20
|
+
## Logic for processing transmitted value
|
|
21
|
+
## Syntax for referencing destination compartment -> component.compartments[destination_compartment]
|
|
22
|
+
|
|
23
|
+
"""
|
|
24
|
+
def overwrite(component, value, destination_compartment):
|
|
25
|
+
"""
|
|
26
|
+
The overwrite bundle rule routine.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
component: target component node that this bundle rule will operate on
|
|
30
|
+
|
|
31
|
+
value: the value to insert into a compartment within the target component
|
|
32
|
+
|
|
33
|
+
destination_compartment: compartment within component to place a value w/in
|
|
34
|
+
"""
|
|
35
|
+
component.compartments[destination_compartment] = value
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def additive(component, value, destination_compartment):
|
|
39
|
+
"""
|
|
40
|
+
The additive/addition bundle rule routine.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
component: target component node that this bundle rule will operate on
|
|
44
|
+
|
|
45
|
+
value: the value to add to current state of compartment w/in target component
|
|
46
|
+
|
|
47
|
+
destination_compartment: compartment within component to add a value to
|
|
48
|
+
"""
|
|
49
|
+
component.compartments[destination_compartment] += value
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def append(component, value, destination_compartment):
|
|
53
|
+
"""
|
|
54
|
+
The append/aggregation bundle rule routine. This is primarily useful if
|
|
55
|
+
the compartment is a list of a values/objects (an appendable list construct).
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
component: target component node that this bundle rule will operate on
|
|
59
|
+
|
|
60
|
+
value: the value to append to current state of compartment w/in target component
|
|
61
|
+
|
|
62
|
+
destination_compartment: compartment within component to append to
|
|
63
|
+
"""
|
|
64
|
+
component.compartments[destination_compartment].append(value)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from .command import Command
|
|
2
|
+
from .advanceState import AdvanceState
|
|
3
|
+
from .clamp import Clamp
|
|
4
|
+
from .evolve import Evolve
|
|
5
|
+
from .reset import Reset
|
|
6
|
+
from .track import Track
|
|
7
|
+
from .save import Save
|
|
8
|
+
from .compound import Compound
|
|
9
|
+
from .snapshot import Snapshot
|
|
10
|
+
from .multiclamp import Multiclamp
|
|
11
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from ngcsimlib.commands import Command
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class AdvanceState(Command):
|
|
5
|
+
"""
|
|
6
|
+
As the general form, all models built in ngclearn are state machines and this
|
|
7
|
+
command is designed to advance the state of all of the components passed into
|
|
8
|
+
the command. Prior to advancing the state of each component, this will call the
|
|
9
|
+
`gather` method of that component.
|
|
10
|
+
"""
|
|
11
|
+
def __init__(self, components=None, command_name=None, **kwargs):
|
|
12
|
+
"""
|
|
13
|
+
Required calls on Components: ['advance_state', 'gather', 'name']
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
components: the list of components to advance the state of
|
|
17
|
+
command_name: the name of the command on the controller
|
|
18
|
+
"""
|
|
19
|
+
super().__init__(components=components, command_name=command_name,
|
|
20
|
+
required_calls=['advance_state', 'gather'])
|
|
21
|
+
|
|
22
|
+
def __call__(self, **kwargs):
|
|
23
|
+
for component in self.components:
|
|
24
|
+
self.components[component].gather()
|
|
25
|
+
self.components[component].advance_state(**kwargs)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from ngcsimlib.commands.command import Command
|
|
2
|
+
from ngcsimlib.utils import extract_args
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Clamp(Command):
|
|
6
|
+
"""
|
|
7
|
+
All components in ngclearn have compartments where they store information
|
|
8
|
+
pertaining to their internal state that can be read into or out of by
|
|
9
|
+
commands. The Clamp command is the primary way to manually set the value of
|
|
10
|
+
a compartment on a set of components. The Clamp command requires a
|
|
11
|
+
compartment that a value can be passed into and be clamped to,
|
|
12
|
+
as well as a `clamp_name` used to locate the value when called.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, components=None, compartment=None, clamp_name=None,
|
|
16
|
+
command_name=None, **kwargs):
|
|
17
|
+
"""
|
|
18
|
+
Required calls on Components: ['clamp', 'name']
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
components: a list of components to call clamp on
|
|
22
|
+
|
|
23
|
+
compartment: the compartment being clamped to
|
|
24
|
+
|
|
25
|
+
clamp_name: a keyword to bind the input for this command do
|
|
26
|
+
|
|
27
|
+
command_name: the name of the command on the controller
|
|
28
|
+
|
|
29
|
+
"""
|
|
30
|
+
super().__init__(components=components, command_name=command_name,
|
|
31
|
+
required_calls=['clamp'])
|
|
32
|
+
if compartment is None:
|
|
33
|
+
raise RuntimeError(
|
|
34
|
+
self.name + " requires a \'compartment\' to clamp to for construction")
|
|
35
|
+
if clamp_name is None:
|
|
36
|
+
raise RuntimeError(
|
|
37
|
+
self.name + " requires a \'clamp_name\' to bind to for construction")
|
|
38
|
+
|
|
39
|
+
self.clamp_name = clamp_name
|
|
40
|
+
self.compartment = compartment
|
|
41
|
+
|
|
42
|
+
for component in self.components:
|
|
43
|
+
if self.compartment not in self.components[component].compartments.keys():
|
|
44
|
+
raise RuntimeError(self.name + " is attempting to "
|
|
45
|
+
"initialize clamp to "
|
|
46
|
+
"non-existent compartment.")
|
|
47
|
+
|
|
48
|
+
def __call__(self, *args, **kwargs):
|
|
49
|
+
try:
|
|
50
|
+
vals = extract_args([self.clamp_name], *args, **kwargs)
|
|
51
|
+
except RuntimeError:
|
|
52
|
+
raise RuntimeError(self.name + ", " + str(
|
|
53
|
+
self.clamp_name) + " is missing from keyword arguments or a positional "
|
|
54
|
+
"arguments can be provided")
|
|
55
|
+
|
|
56
|
+
for component in self.components:
|
|
57
|
+
self.components[component].clamp(self.compartment, vals[self.clamp_name])
|
|
58
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from ngcsimlib.utils import check_attributes
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Command(ABC):
|
|
6
|
+
"""
|
|
7
|
+
The base class for all commands found in ngcsimlib. At its core, a command is
|
|
8
|
+
essentially a method to be called by the controller that affects the
|
|
9
|
+
components in a simulated complex system / model in some way. When a command
|
|
10
|
+
is made, a preprocessing step is run to verify that all of the needed
|
|
11
|
+
attributes are present on each component. Note that this step does not
|
|
12
|
+
ensure types or values, just that they do or do not exist.
|
|
13
|
+
"""
|
|
14
|
+
def __init__(self, components=None, command_name=None, required_calls=None):
|
|
15
|
+
"""
|
|
16
|
+
Required calls on Components: ['name']
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
components: a list of components to run the command on
|
|
20
|
+
|
|
21
|
+
required_calls: a list of required attributes for all components
|
|
22
|
+
|
|
23
|
+
command_name: the name of the command on the controller
|
|
24
|
+
"""
|
|
25
|
+
self.name = str(command_name)
|
|
26
|
+
self.components = {}
|
|
27
|
+
required_calls = ['name'] if required_calls is None else required_calls + ['name']
|
|
28
|
+
for comp in components:
|
|
29
|
+
if check_attributes(comp, required_calls, fatal=True):
|
|
30
|
+
self.components[comp.name] = comp
|
|
31
|
+
|
|
32
|
+
@abstractmethod
|
|
33
|
+
def __call__(self, *args, **kwargs):
|
|
34
|
+
pass
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from ngcsimlib.commands import Command
|
|
2
|
+
from ngcsimlib.utils import check_attributes
|
|
3
|
+
import warnings
|
|
4
|
+
|
|
5
|
+
class Compound(Command):
|
|
6
|
+
"""
|
|
7
|
+
There is sometimes a need by controllers to be able to call a set of commands
|
|
8
|
+
in series without writing custom command(s) for each combination. The
|
|
9
|
+
compound node/command is used to fill this need. A compound command set is
|
|
10
|
+
very similar to the cycle found inside/within a controller / simulation object.
|
|
11
|
+
"""
|
|
12
|
+
def __init__(self, components=None, command_name=None, command_list=None,
|
|
13
|
+
controller=None, **kwargs):
|
|
14
|
+
"""
|
|
15
|
+
Constructs a compound command construct.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
components: a list of components, this will go unused by default
|
|
19
|
+
|
|
20
|
+
command_name: the name of the compound command
|
|
21
|
+
|
|
22
|
+
command_list: a list of all commands to be called
|
|
23
|
+
|
|
24
|
+
controller: the controller that will be calling these commands
|
|
25
|
+
"""
|
|
26
|
+
super().__init__(components=components, command_name=command_name)
|
|
27
|
+
if controller is None:
|
|
28
|
+
raise RuntimeError("The controller is needed to build a compound command (This should be passed in by default)")
|
|
29
|
+
if command_list is None or len(command_list) == 0:
|
|
30
|
+
warnings.warn("The command list for command " + self.name + " is None or empty")
|
|
31
|
+
|
|
32
|
+
self.command_list = command_list
|
|
33
|
+
self.controller = controller
|
|
34
|
+
|
|
35
|
+
check_attributes(self.controller, self.command_list, fatal=True)
|
|
36
|
+
|
|
37
|
+
def __call__(self, *args, **kwargs):
|
|
38
|
+
for command in self.command_list:
|
|
39
|
+
self.controller.runCommand(command, *args, **kwargs)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from ngcsimlib.commands import Command
|
|
2
|
+
from ngcsimlib.utils import extract_args
|
|
3
|
+
|
|
4
|
+
class Evolve(Command):
|
|
5
|
+
"""
|
|
6
|
+
In many models, there is a need to have either a backward pass or a separate
|
|
7
|
+
method to update some particular internal state (value), e.g., a "learning"
|
|
8
|
+
or evolutionary change that is not related to central compartment states.
|
|
9
|
+
In general, this can be mapped to a call of `evolve`. Like with
|
|
10
|
+
`advanceState`, this will call the gather method prior to calling the
|
|
11
|
+
`evolve` function of every component.
|
|
12
|
+
|
|
13
|
+
"""
|
|
14
|
+
def __init__(self, components=None, frozen_flag=None, command_name=None,
|
|
15
|
+
**kwargs):
|
|
16
|
+
"""
|
|
17
|
+
Required calls on Components: ['evolve', 'gather', 'name']
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
components: the list of components to evolve
|
|
21
|
+
|
|
22
|
+
frozen_flag: the keyword for the flag to freeze this evolve step
|
|
23
|
+
|
|
24
|
+
command_name: the name of the command on the controller
|
|
25
|
+
"""
|
|
26
|
+
super().__init__(components=components, command_name=command_name,
|
|
27
|
+
required_calls=['evolve'])
|
|
28
|
+
|
|
29
|
+
self.frozen_flag = frozen_flag
|
|
30
|
+
|
|
31
|
+
def __call__(self, *args, **kwargs):
|
|
32
|
+
vals = {}
|
|
33
|
+
try:
|
|
34
|
+
vals = extract_args([self.frozen_flag], *args, **kwargs)
|
|
35
|
+
except RuntimeError:
|
|
36
|
+
vals[self.frozen_flag] = False
|
|
37
|
+
|
|
38
|
+
if not vals[self.frozen_flag]:
|
|
39
|
+
for component in self.components:
|
|
40
|
+
self.components[component].gather()
|
|
41
|
+
self.components[component].evolve(**kwargs)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from ngcsimlib.commands.command import Command
|
|
2
|
+
from ngcsimlib.utils import extract_args, check_attributes
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Multiclamp(Command):
|
|
6
|
+
"""
|
|
7
|
+
There are times when a model will have many clamp calls as there might be a
|
|
8
|
+
need to clamp many different values to a model at the same time. As a
|
|
9
|
+
solution to this, ngcsimlib provides the `multiclamp` command. This command is
|
|
10
|
+
used to set a wide range of values to all compartments with the same name
|
|
11
|
+
across all provided compartments.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, components=None, clamp_name=None, command_name=None,
|
|
15
|
+
**kwargs):
|
|
16
|
+
"""
|
|
17
|
+
Required calls on Components: ['clamp', 'name']
|
|
18
|
+
Args:
|
|
19
|
+
components: a list of components to call clamp on
|
|
20
|
+
|
|
21
|
+
clamp_name: a keyword to bind the input for this command do
|
|
22
|
+
|
|
23
|
+
command_name: the name of the command on the controller
|
|
24
|
+
"""
|
|
25
|
+
super().__init__(components=components, command_name=command_name,
|
|
26
|
+
required_calls=['clamp'])
|
|
27
|
+
if clamp_name is None:
|
|
28
|
+
raise RuntimeError(self.name + " requires a \'clamp_name\' to bind to for construction")
|
|
29
|
+
|
|
30
|
+
self.clamp_name = clamp_name
|
|
31
|
+
|
|
32
|
+
def __call__(self, *args, **kwargs):
|
|
33
|
+
try:
|
|
34
|
+
vals = extract_args([self.clamp_name], *args, **kwargs)
|
|
35
|
+
except RuntimeError:
|
|
36
|
+
raise RuntimeError(self.name + ", " + str(self.clamp_name) + " is missing from keyword arguments or a "
|
|
37
|
+
"positional arguments can be provided")
|
|
38
|
+
|
|
39
|
+
for compartment, value in vals[self.clamp_name].items():
|
|
40
|
+
for component in self.components:
|
|
41
|
+
if check_attributes(component, compartment, fatal=False):
|
|
42
|
+
self.components[component].clamp(compartment, value)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from ngcsimlib.commands import Command
|
|
2
|
+
from ngcsimlib.utils import extract_args
|
|
3
|
+
import warnings
|
|
4
|
+
|
|
5
|
+
class Reset(Command):
|
|
6
|
+
"""
|
|
7
|
+
In every model/system, there is a need to reset components back to some
|
|
8
|
+
intial state value(s). As such, many components that maintain a state have a
|
|
9
|
+
reset method implemented within them. The reset command will go through
|
|
10
|
+
the list of components and trigger the reset within each of them.
|
|
11
|
+
"""
|
|
12
|
+
def __init__(self, components=None, reset_name=None, command_name=None,
|
|
13
|
+
**kwargs):
|
|
14
|
+
"""
|
|
15
|
+
Required calls on Components: ['reset', 'name']
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
components: a list of components to reset
|
|
19
|
+
|
|
20
|
+
reset_name: the keyword for the flag on if the reset should happen
|
|
21
|
+
|
|
22
|
+
command_name: the name of the command on the controller
|
|
23
|
+
"""
|
|
24
|
+
super().__init__(components=components, command_name=command_name,
|
|
25
|
+
required_calls=['reset'])
|
|
26
|
+
if reset_name is None:
|
|
27
|
+
raise RuntimeError(self.name + " requires a \'reset_name\' to bind to for construction")
|
|
28
|
+
self.reset_name = reset_name
|
|
29
|
+
|
|
30
|
+
def __call__(self, *args, **kwargs):
|
|
31
|
+
try:
|
|
32
|
+
vals = extract_args([self.reset_name], *args, **kwargs)
|
|
33
|
+
except RuntimeError:
|
|
34
|
+
warnings.warn(self.name + ", " + str(self.reset_name) + " is missing from keyword arguments and no "
|
|
35
|
+
"positional arguments were provided", stacklevel=6)
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
if vals[self.reset_name]:
|
|
39
|
+
for component in self.components:
|
|
40
|
+
self.components[component].reset()
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from ngcsimlib.commands import Command
|
|
2
|
+
from ngcsimlib.utils import extract_args
|
|
3
|
+
import warnings
|
|
4
|
+
|
|
5
|
+
class Save(Command):
|
|
6
|
+
"""
|
|
7
|
+
When training models, there is often a need to snapshot the model and save
|
|
8
|
+
it to disk. The base controller in ngcsimlib is able to save all of the
|
|
9
|
+
commands, components, connections, and steps to a file in order to rebuild
|
|
10
|
+
the model at a later time. However, there is a good chance that the model
|
|
11
|
+
will contain components that have parts that need saving beyond the parameters
|
|
12
|
+
passed in to initialize the component. ngcsimlib solves this by providing a
|
|
13
|
+
save command; this command will call a custom save method on all components
|
|
14
|
+
provided to the command. This custom save method will be responsible for all
|
|
15
|
+
custom values to be saved and determining where to save them inside of a
|
|
16
|
+
provided directory.
|
|
17
|
+
"""
|
|
18
|
+
def __init__(self, components=None, directory_flag=None, command_name=None,
|
|
19
|
+
**kwargs):
|
|
20
|
+
"""
|
|
21
|
+
Required calls on Components: ['save', 'name']
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
components: a list of components to call the save function on
|
|
25
|
+
|
|
26
|
+
directory_flag: keyword for flag for the directory to save to
|
|
27
|
+
|
|
28
|
+
command_name: the name of the command on the controller
|
|
29
|
+
"""
|
|
30
|
+
super().__init__(components=components, command_name=command_name,
|
|
31
|
+
required_calls=['save'])
|
|
32
|
+
if directory_flag is None:
|
|
33
|
+
raise RuntimeError(self.name + " requires a \'directory_flag\' to bind to for construction")
|
|
34
|
+
self.directory_flag = directory_flag
|
|
35
|
+
|
|
36
|
+
def __call__(self, *args, **kwargs):
|
|
37
|
+
try:
|
|
38
|
+
vals = extract_args([self.directory_flag], *args, **kwargs)
|
|
39
|
+
except RuntimeError:
|
|
40
|
+
warnings.warn(self.name + ", " + str(self.directory_flag) + " is missing from keyword arguments and no "
|
|
41
|
+
"positional arguments were provided", stacklevel=6)
|
|
42
|
+
return
|
|
43
|
+
|
|
44
|
+
if vals[self.directory_flag]:
|
|
45
|
+
for component in self.components:
|
|
46
|
+
self.components[component].save(vals[self.directory_flag])
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from ngcsimlib.commands import Command
|
|
2
|
+
|
|
3
|
+
class Snapshot(Command):
|
|
4
|
+
"""
|
|
5
|
+
Sometimes when running through a model, there is a need to extract a value
|
|
6
|
+
from a compartment to the run loop. As such, ngcsimlib provides the snapshot
|
|
7
|
+
command. This command will extract a given attribute from all components
|
|
8
|
+
provided to the command. This command returns a single value if only one
|
|
9
|
+
component is given, otherwise it will return a list where each value
|
|
10
|
+
corresponds to the position of the component in the components list.
|
|
11
|
+
|
|
12
|
+
This command is regularly used for debugging/graph production code that
|
|
13
|
+
exists outside of the model. It would be incorrect to clamp the output of
|
|
14
|
+
this command into another component, if that is the intended goal, please
|
|
15
|
+
see `connect` in the controller.
|
|
16
|
+
"""
|
|
17
|
+
def __init__(self, components=None, attribute=None, command_name=None,
|
|
18
|
+
**kwargs):
|
|
19
|
+
"""
|
|
20
|
+
Required calls on Components: ['name'], and the passed-in attribute
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
components: the component extract the values of
|
|
24
|
+
|
|
25
|
+
attribute: a single attribute to return
|
|
26
|
+
|
|
27
|
+
command_name: the name of the command on the controller
|
|
28
|
+
"""
|
|
29
|
+
super().__init__(components=components, command_name=command_name,
|
|
30
|
+
required_calls=[attribute])
|
|
31
|
+
self.attribute = attribute
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def __call__(self, *args, **kwargs):
|
|
35
|
+
vals = []
|
|
36
|
+
for component in self.components:
|
|
37
|
+
vals.append(getattr(self.components[component], self.attribute))
|
|
38
|
+
|
|
39
|
+
return vals if len(vals) > 1 else vals[0]
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from ngcsimlib.commands import Command
|
|
2
|
+
from ngcsimlib.utils import extract_args
|
|
3
|
+
import warnings
|
|
4
|
+
|
|
5
|
+
class Track(Command):
|
|
6
|
+
"""
|
|
7
|
+
When running a model or complex system, there is often a need to track a
|
|
8
|
+
compartment value over time, usually for visualization. To do this, ngcsimlib
|
|
9
|
+
provides a track utility command. This command stores the values of a
|
|
10
|
+
compartment from a set of components into a provided object. This provided
|
|
11
|
+
object is expected to have an `.append` method implemented and each element
|
|
12
|
+
appended to this object will be the values of the compartment for each
|
|
13
|
+
provided component, in the same order that they were provided in.
|
|
14
|
+
"""
|
|
15
|
+
def __init__(self, components=None, compartment=None, tracker=None,
|
|
16
|
+
command_name=None, **kwargs):
|
|
17
|
+
"""
|
|
18
|
+
Required calls on Components: ['name']
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
components: a list of components to track values from
|
|
22
|
+
|
|
23
|
+
compartment: the compartment to extract information from
|
|
24
|
+
|
|
25
|
+
tracker: the keyword for which the tracking object will be passed in by
|
|
26
|
+
|
|
27
|
+
command_name: the name of the command on the controller
|
|
28
|
+
"""
|
|
29
|
+
super().__init__(components=components, command_name=command_name)
|
|
30
|
+
if compartment is None:
|
|
31
|
+
raise RuntimeError(self.name + " requires a \'compartment\' to clamp to for construction")
|
|
32
|
+
if tracker is None:
|
|
33
|
+
raise RuntimeError(self.name + " requires a \'tracker\' to bind to for construction")
|
|
34
|
+
|
|
35
|
+
self.tracker = tracker
|
|
36
|
+
self.compartment = compartment
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def __call__(self, *args, **kwargs):
|
|
40
|
+
try:
|
|
41
|
+
vals = extract_args([self.tracker], *args, **kwargs)
|
|
42
|
+
except RuntimeError:
|
|
43
|
+
warnings.warn(self.name + ", " + str(self.tracker) + " is missing from keyword arguments and no "
|
|
44
|
+
"positional arguments were provided", stacklevel=6)
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
v = []
|
|
48
|
+
for component in self.components:
|
|
49
|
+
v.append(self.components[component].compartments[self.compartment])
|
|
50
|
+
vals[self.tracker].append(v)
|