fmdtools 2.2.2__tar.gz → 2.2.3__tar.gz

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.
Files changed (104) hide show
  1. {fmdtools-2.2.2 → fmdtools-2.2.3}/LICENSE +1 -1
  2. {fmdtools-2.2.2 → fmdtools-2.2.3}/PKG-INFO +5 -5
  3. {fmdtools-2.2.2 → fmdtools-2.2.3}/README.md +3 -3
  4. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/multirotor/drone_mdl_dynamic.py +2 -1
  5. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/multirotor/drone_mdl_hierarchical.py +1 -2
  6. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/multirotor/drone_mdl_static.py +4 -0
  7. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/__init__.py +1 -1
  8. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/analyze/common.py +1 -1
  9. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/define/architecture/base.py +21 -5
  10. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/define/block/base.py +3 -2
  11. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/define/container/base.py +2 -0
  12. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/define/environment.py +26 -6
  13. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/define/flow/commsflow.py +3 -1
  14. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/define/flow/multiflow.py +8 -1
  15. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/define/object/base.py +3 -3
  16. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/define/object/coords.py +1 -0
  17. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools.egg-info/PKG-INFO +5 -5
  18. {fmdtools-2.2.2 → fmdtools-2.2.3}/pyproject.toml +1 -1
  19. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/__init__.py +0 -0
  20. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/asg_demo/__init__.py +0 -0
  21. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/asg_demo/demo_model.py +0 -0
  22. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/eps/__init__.py +0 -0
  23. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/eps/eps.py +0 -0
  24. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/eps/test_eps.py +0 -0
  25. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/multiflow_demo/__init__.py +0 -0
  26. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/multiflow_demo/multiflow_demo.py +0 -0
  27. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/multiflow_demo/test_multiflow.py +0 -0
  28. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/multirotor/__init__.py +0 -0
  29. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/multirotor/drone_mdl_rural.py +0 -0
  30. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/multirotor/drone_mdl_urban.py +0 -0
  31. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/multirotor/opt_drone_rural.py +0 -0
  32. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/multirotor/test_multirotor.py +0 -0
  33. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/pump/__init__.py +0 -0
  34. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/pump/ex_pump.py +0 -0
  35. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/pump/jointfaults_ex.py +0 -0
  36. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/pump/parallelism_methods.py +0 -0
  37. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/pump/pump_indiv.py +0 -0
  38. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/pump/pump_stochastic.py +0 -0
  39. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/pump/test_pump.py +0 -0
  40. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/pump/test_pump_example.py +0 -0
  41. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/pump/test_pump_stochastic.py +0 -0
  42. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/rover/__init__.py +0 -0
  43. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/rover/optimization/ea_analysis.py +0 -0
  44. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/rover/optimization/search_rover.py +0 -0
  45. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/rover/rover_degradation.py +0 -0
  46. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/rover/rover_mode_space.py +0 -0
  47. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/rover/rover_model.py +0 -0
  48. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/rover/rover_model_human.py +0 -0
  49. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/rover/test_rover.py +0 -0
  50. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/tank/__init__.py +0 -0
  51. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/tank/tank_model.py +0 -0
  52. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/tank/tank_optimization_model.py +0 -0
  53. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/tank/test_tank.py +0 -0
  54. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/taxiway/ATC.py +0 -0
  55. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/taxiway/asset.py +0 -0
  56. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/taxiway/common.py +0 -0
  57. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/taxiway/model.py +0 -0
  58. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/taxiway/test_asset.py +0 -0
  59. {fmdtools-2.2.2 → fmdtools-2.2.3}/examples/taxiway/test_model.py +0 -0
  60. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/analyze/__init__.py +0 -0
  61. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/analyze/graph/__init__.py +0 -0
  62. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/analyze/graph/base.py +0 -0
  63. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/analyze/graph/label.py +0 -0
  64. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/analyze/graph/model.py +0 -0
  65. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/analyze/graph/style.py +0 -0
  66. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/analyze/history.py +0 -0
  67. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/analyze/phases.py +0 -0
  68. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/analyze/result.py +0 -0
  69. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/analyze/tabulate.py +0 -0
  70. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/define/__init__.py +0 -0
  71. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/define/architecture/__init__.py +0 -0
  72. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/define/architecture/action.py +0 -0
  73. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/define/architecture/component.py +0 -0
  74. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/define/architecture/function.py +0 -0
  75. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/define/architecture/geom.py +0 -0
  76. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/define/base.py +0 -0
  77. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/define/block/__init__.py +0 -0
  78. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/define/block/action.py +0 -0
  79. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/define/block/component.py +0 -0
  80. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/define/block/function.py +0 -0
  81. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/define/container/__init__.py +0 -0
  82. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/define/container/mode.py +0 -0
  83. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/define/container/parameter.py +0 -0
  84. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/define/container/rand.py +0 -0
  85. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/define/container/state.py +0 -0
  86. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/define/container/time.py +0 -0
  87. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/define/flow/__init__.py +0 -0
  88. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/define/flow/base.py +0 -0
  89. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/define/object/__init__.py +0 -0
  90. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/define/object/geom.py +0 -0
  91. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/define/object/timer.py +0 -0
  92. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/sim/__init__.py +0 -0
  93. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/sim/propagate.py +0 -0
  94. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/sim/sample.py +0 -0
  95. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/sim/scenario.py +0 -0
  96. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools/sim/search.py +0 -0
  97. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools.egg-info/SOURCES.txt +0 -0
  98. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools.egg-info/dependency_links.txt +0 -0
  99. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools.egg-info/requires.txt +0 -0
  100. {fmdtools-2.2.2 → fmdtools-2.2.3}/fmdtools.egg-info/top_level.txt +0 -0
  101. {fmdtools-2.2.2 → fmdtools-2.2.3}/setup.cfg +0 -0
  102. {fmdtools-2.2.2 → fmdtools-2.2.3}/tests/test_graph.py +0 -0
  103. {fmdtools-2.2.2 → fmdtools-2.2.3}/tests/test_hierarchical.py +0 -0
  104. {fmdtools-2.2.2 → fmdtools-2.2.3}/tests/test_packages.py +0 -0
@@ -1,4 +1,4 @@
1
- fmdtools version 2.2.2
1
+ fmdtools version 2.2.3
2
2
  ---------------------------
3
3
  Apache License
4
4
  Version 2.0, January 2004
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fmdtools
3
- Version: 2.2.2
3
+ Version: 2.2.3
4
4
  Summary: System resilience modelling, simulation, and assessment in Python
5
5
  Author: fmdtools Developers
6
6
  Maintainer-email: Daniel Hulse <daniel.e.hulse@nasa.gov>
7
- License: fmdtools version 2.2.2
7
+ License: fmdtools version 2.2.3
8
8
  ---------------------------
9
9
  Apache License
10
10
  Version 2.0, January 2004
@@ -367,7 +367,7 @@ This may then be installed using:
367
367
  Note that this version reflects the latest version on the ``main`` git branch along with any local changes. To use a particular version of ``fmdtools`` you can checkout the tag corresponding to the version you want, e.g.:
368
368
 
369
369
  ```
370
- git checkout v2.2.1
370
+ git checkout v2.2.3
371
371
  ```
372
372
 
373
373
 
@@ -505,8 +505,8 @@ To cite a particular version of the fmdtools, you may use:
505
505
  author = {{NASA}},
506
506
  title = {fmdtools},
507
507
  url = {https://github.com/nasa/fmdtools},
508
- version = {2.2.1},
509
- date = {2025-11-24},
508
+ version = {2.2.3},
509
+ date = {2025-12-4},
510
510
  }
511
511
  ```
512
512
 
@@ -106,7 +106,7 @@ This may then be installed using:
106
106
  Note that this version reflects the latest version on the ``main`` git branch along with any local changes. To use a particular version of ``fmdtools`` you can checkout the tag corresponding to the version you want, e.g.:
107
107
 
108
108
  ```
109
- git checkout v2.2.1
109
+ git checkout v2.2.3
110
110
  ```
111
111
 
112
112
 
@@ -244,8 +244,8 @@ To cite a particular version of the fmdtools, you may use:
244
244
  author = {{NASA}},
245
245
  title = {fmdtools},
246
246
  url = {https://github.com/nasa/fmdtools},
247
- version = {2.2.1},
248
- date = {2025-11-24},
247
+ version = {2.2.3},
248
+ date = {2025-12-4},
249
249
  }
250
250
  ```
251
251
 
@@ -451,7 +451,8 @@ class Drone(FunctionArchitecture):
451
451
  totcost = repcost + crashcost + lostcost
452
452
  rate = scen.rate
453
453
  expcost = totcost*rate*1e5
454
- return {'rate': rate, 'cost': totcost, 'expected_cost': expcost}
454
+ return {'rate': rate, 'cost': totcost, 'expected_cost': expcost,
455
+ 'lost_cost': lostcost, 'crashcost': crashcost}
455
456
 
456
457
 
457
458
  def finddist(p1, p2):
@@ -348,7 +348,7 @@ if __name__ == "__main__":
348
348
  import multiprocessing as mp
349
349
  from fmdtools.sim.sample import FaultDomain, FaultSample
350
350
  from fmdtools.analyze.phases import PhaseMap
351
- """
351
+
352
352
  # check rf_mechbreack fault propagation in quad architecture:
353
353
  hierarchical_model = Drone(p=DroneParam(arch='quad'))
354
354
  result, mdlhist = fs.propagate.one_fault(hierarchical_model,
@@ -379,4 +379,3 @@ if __name__ == "__main__":
379
379
  fig, ax = hist.plot_trajectories("dofs.s.x", "dofs.s.y", "dofs.s.z",
380
380
  time_groups=['nominal'],
381
381
  indiv_kwargs={'faulty': fault_kwargs})
382
- """
@@ -795,4 +795,8 @@ if __name__ == "__main__":
795
795
  doctest.testmod(verbose=True)
796
796
 
797
797
  static_mdl = Drone()
798
+ fig, ax = static_mdl.as_modelgraph().draw()
799
+
800
+ result, history = propagate.one_fault(static_mdl, "store_ee", "nocharge")
801
+
798
802
  results, mdlhists = propagate.single_faults(static_mdl)
@@ -22,6 +22,6 @@ import fmdtools.sim as sim
22
22
  import fmdtools.analyze as analyze
23
23
 
24
24
  __all__ = ['analyze', 'sim', 'define']
25
- __version__ = '2.2.2'
25
+ __version__ = '2.2.3'
26
26
  __copyright__ = "Copyright 2024 United States Government"
27
27
  __license__ = "Apache-2.0"
@@ -718,7 +718,7 @@ def consolidate_legend(ax, loc='upper left', bbox_to_anchor=(1.05, 1),
718
718
 
719
719
  # generate legend with consolidated labels/handles
720
720
  ax.legend(by_label.values(), by_label.keys(),
721
- bbox_to_anchor=bbox_to_anchor, loc=loc, **kwargs)
721
+ bbox_to_anchor=bbox_to_anchor, loc=loc, **filter_kwargs(ax.legend, **kwargs))
722
722
 
723
723
 
724
724
  def mark_times(ax, tick, time, *plot_values, fontsize=8, rounddec=1, pretext="t="):
@@ -23,6 +23,7 @@ specific language governing permissions and limitations under the License.
23
23
 
24
24
  from fmdtools.define.object.base import check_pickleability, BaseObject, get_dict_repr
25
25
  from fmdtools.define.flow.base import Flow
26
+ from fmdtools.define.flow.multiflow import MultiFlow
26
27
  from fmdtools.define.block.base import Simulable
27
28
  from fmdtools.define.object.base import init_obj, get_obj_name
28
29
  from fmdtools.analyze.common import get_sub_include
@@ -295,8 +296,8 @@ class Architecture(Simulable):
295
296
  Name of the object
296
297
  objclass : class, optional
297
298
  Class to instantiate in the dict. The default is BaseObject.
298
- as_copy : bool
299
- Whether to instantiate obj as a copy. The default is fault.
299
+ use_copy : bool
300
+ Whether to use existing copy in self.flows. The default is False.
300
301
  **kwargs : kwargs
301
302
  Non-default kwargs to send to the object class.
302
303
  """
@@ -579,7 +580,8 @@ class Architecture(Simulable):
579
580
  copy : Architecture
580
581
  Copy of the curent architecture.
581
582
  """
582
- cargs = dict(p=getattr(self, 'p', {}),
583
+ cargs = dict(root=self.root,
584
+ p=getattr(self, 'p', {}),
583
585
  sp=getattr(self, 'sp', {}),
584
586
  track=getattr(self, 'track', {}),
585
587
  h=self.h.copy(),
@@ -590,8 +592,7 @@ class Architecture(Simulable):
590
592
  cargs[flex_role+'s'] = getattr(self, flex_role+'s')
591
593
  # if flows provided from above, use those flows. Otherwise copy own.
592
594
  if hasattr(self, 'flows'):
593
- cargs['flows'] = {f: flows[f] if f in flows else obj.copy()
594
- for f, obj in self.flows.items()}
595
+ cargs['flows'] = self.copy_flows(flows=flows)
595
596
 
596
597
  if hasattr(self, 'r'):
597
598
  cargs['r'] = self.r.copy()
@@ -599,6 +600,21 @@ class Architecture(Simulable):
599
600
  cop.assign_roles('container', self)
600
601
  return cop
601
602
 
603
+ def copy_flows(self, flows={}):
604
+ """Copy flows, along with MultiFlow structures if present."""
605
+ globs = {}
606
+ cflows = {}
607
+ for fn, flow in self.flows.items():
608
+ if fn in flows:
609
+ cflows[fn] = flows[fn]
610
+ elif not isinstance(flow, MultiFlow) or flow.glob.name == flow.name:
611
+ cflows[fn] = flow.copy()
612
+ else:
613
+ if flow.glob.name not in globs:
614
+ globs[flow.glob.name] = flow.glob.copy()
615
+ cflows[fn] = globs[flow.glob.name].get_view(fn)
616
+ return cflows
617
+
602
618
  def get_all_possible_track(self):
603
619
  return super().get_all_possible_track() + self.flexible_roles
604
620
 
@@ -28,6 +28,7 @@ from fmdtools.define.container.parameter import Parameter
28
28
  from fmdtools.define.container.time import Time
29
29
  from fmdtools.define.container.mode import Fault
30
30
  from fmdtools.analyze.result import Result
31
+ from fmdtools.analyze.history import History
31
32
  from fmdtools.analyze.graph.base import Graph
32
33
 
33
34
  import itertools
@@ -224,7 +225,7 @@ class Simulable(BaseObject):
224
225
 
225
226
  def init_hist(self, h={}):
226
227
  """Initialize the history of the sim using SimParam parameters and track."""
227
- if not h:
228
+ if not isinstance(h, History):
228
229
  timerange = self.sp.get_histrange()
229
230
  self.h = self.create_hist(timerange)
230
231
  else:
@@ -1001,7 +1002,7 @@ class Block(Simulable):
1001
1002
  if 'arch' in self.roletypes:
1002
1003
  arch_dict = {role: getattr(self, role)
1003
1004
  for role in self.get_roles('arch')}
1004
- paramdict = {**paramdict, 'sp': self.sp, **arch_dict}
1005
+ paramdict = {**paramdict, 'sp': self.sp, 'root': self.root, **arch_dict}
1005
1006
  cop = self.__class__(self.name, *args, flows=flows, **paramdict)
1006
1007
  cop.assign_roles('container', self)
1007
1008
  except TypeError as e:
@@ -317,6 +317,8 @@ class BaseContainer(dataobject, mapping=True, iterable=True, copy_default=True):
317
317
  try:
318
318
  true_type = self.__annotations__[fieldname]
319
319
  super().__setattr__(fieldname, true_type(value))
320
+ except AttributeError:
321
+ super().__setattr__(fieldname, value)
320
322
  except TypeError as e:
321
323
  raise Exception("Poorly Specified field " + fieldname +
322
324
  " in class " + self.__class__.__name__) from e
@@ -81,13 +81,16 @@ class Environment(CommsFlow):
81
81
  # NOTE: p and s also init here because if not, they are overritten
82
82
  # may need to change in the future
83
83
  self.init_roletypes('container', "coords", "arch", r=r, p=p, s=s, sp=sp)
84
- if 'p' not in c and getattr(self.coords_c, 'container_p', None) == getattr(self, 'container_p', None):
85
- c = {**c, 'p': p}
86
- if 'p' not in ga and getattr(self.arch_ga, 'container_p', None) == getattr(self, 'container_p', None):
87
- ga = {**ga, 'p': p}
88
84
  r_kwargs = {'run_stochastic': self.r.run_stochastic, 'seed': self.r.seed}
89
- c = {**{'r': r_kwargs}, **c}
90
- ga = {**{'r': r_kwargs, 'sp': self.sp.get_sub_kwargs()}, **ga}
85
+ if isinstance(c, dict):
86
+ if 'p' not in c and getattr(self.coords_c, 'container_p', None) == getattr(self, 'container_p', None):
87
+ c = {**c, 'p': p}
88
+ c = {**{'r': r_kwargs}, **c}
89
+ if isinstance(ga, dict):
90
+ if 'p' not in ga and getattr(self.arch_ga, 'container_p', None) == getattr(self, 'container_p', None):
91
+ ga = {**ga, 'p': p}
92
+ ga = {**{'r': r_kwargs, 'sp': self.sp.get_sub_kwargs()}, **ga}
93
+
91
94
  self.init_roletypes('coords', 'arch', c=c, ga=ga)
92
95
 
93
96
  def base_type(self):
@@ -129,6 +132,15 @@ class Environment(CommsFlow):
129
132
  >>> d.ga.lines['ex_line'].s.occupied = True
130
133
  >>> e.ga.lines['ex_line'].s.occupied
131
134
  False
135
+
136
+ This should also be the case for contained local versions:
137
+
138
+ >>> e = ExampleEnvironment("env")
139
+ >>> hi = e.create_local("hi")
140
+ >>> e.hi.ga.points['ex_point'].s.occupied=True
141
+ >>> d = e.copy()
142
+ >>> d.hi.ga.points['ex_point'].s.occupied
143
+ True
132
144
  """
133
145
  cop = super().copy(glob=glob, p=p, s=s)
134
146
  cop.r.assign(self.r)
@@ -158,6 +170,14 @@ class ExampleEnvironment(Environment):
158
170
 
159
171
  if __name__ == "__main__":
160
172
  e = ExampleEnvironment("env")
173
+ e.create_local("hi")
174
+ e.hi.ga.points['ex_point'].s.occupied=True
161
175
  d = e.copy()
176
+ d.hi.ga.points['ex_point'].s.occupied
177
+
178
+
179
+ c = ExampleEnvironment(track=None)
180
+ b = c.copy()
181
+ b.ga.h
162
182
  import doctest
163
183
  doctest.testmod(verbose=True)
@@ -147,7 +147,9 @@ class CommsFlow(MultiFlow):
147
147
 
148
148
  def send(self, fxn_to, *states, fxn_from="local"):
149
149
  """
150
- Sends a function's (fxn_from) view for the CommsFlow to another function fxn_to
150
+ Send a function's view to another function.
151
+
152
+ Sends function's (fxn_from) view for the CommsFlow to another function fxn_to
151
153
  by updating the function's out property and fxn_to's inbox list. Note that the
152
154
  other function must call recieve on the other end for the message to be fully
153
155
  received (update its internal view).
@@ -284,10 +284,17 @@ class MultiFlow(Flow):
284
284
  """Copy the flow and create new sub-flow copies for it."""
285
285
  if not s and hasattr(self, 's'):
286
286
  s = self.s.asdict()
287
+ if not glob and self.glob.name != self.name:
288
+ raise Exception("Unable to copy "+self.name+" without "+self.glob.name+".")
287
289
  cop = self.__class__(self.name, glob=glob, p=p, s=s, track=track, root=self.root)
290
+ if hasattr(self, 'h'):
291
+ cop.h = self.h.copy()
288
292
  for loc in self.locals:
289
293
  local = getattr(self, loc)
290
- cop.create_local(local.name, **local.copy_mut_containers(), as_copy=True)
294
+ mutes = local.copy_mutes(exclude=['local'])
295
+ cl = cop.create_local(local.name, **mutes, as_copy=True)
296
+ if hasattr(cl, 'h'):
297
+ cop.h[loc] = cl.h
291
298
  return cop
292
299
 
293
300
  def create_hist(self, timerange, track=None):
@@ -620,11 +620,11 @@ class BaseObject(metaclass=BaseType):
620
620
  return self.get_roles_as_dict(*flexible_roles, with_flex=True,
621
621
  with_prefixes=flex_prefixes)
622
622
 
623
- def copy_mut_containers(self):
623
+ def copy_mutes(self, *mute_types, **kwargs):
624
624
  """Return copies of the mutable containers."""
625
625
  return {k: v.copy()
626
- for k, v in self.get_roles_as_dict('container',
627
- with_immutable=False).items()}
626
+ for k, v in self.get_roles_as_dict(*mute_types, with_immutable=False,
627
+ **kwargs).items()}
628
628
 
629
629
  def get_roles_as_dict(self, *roletypes, with_immutable=True, with_prefix=False,
630
630
  with_flex=True, exclude=[], **kwargs):
@@ -1689,6 +1689,7 @@ class MetricCoords(BaseCoords):
1689
1689
  array([[0, 1],
1690
1690
  [0, 4]])
1691
1691
  """
1692
+
1692
1693
  __slots__ = ('__dict__', )
1693
1694
  roletypes = ['container', 'value']
1694
1695
 
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fmdtools
3
- Version: 2.2.2
3
+ Version: 2.2.3
4
4
  Summary: System resilience modelling, simulation, and assessment in Python
5
5
  Author: fmdtools Developers
6
6
  Maintainer-email: Daniel Hulse <daniel.e.hulse@nasa.gov>
7
- License: fmdtools version 2.2.2
7
+ License: fmdtools version 2.2.3
8
8
  ---------------------------
9
9
  Apache License
10
10
  Version 2.0, January 2004
@@ -367,7 +367,7 @@ This may then be installed using:
367
367
  Note that this version reflects the latest version on the ``main`` git branch along with any local changes. To use a particular version of ``fmdtools`` you can checkout the tag corresponding to the version you want, e.g.:
368
368
 
369
369
  ```
370
- git checkout v2.2.1
370
+ git checkout v2.2.3
371
371
  ```
372
372
 
373
373
 
@@ -505,8 +505,8 @@ To cite a particular version of the fmdtools, you may use:
505
505
  author = {{NASA}},
506
506
  title = {fmdtools},
507
507
  url = {https://github.com/nasa/fmdtools},
508
- version = {2.2.1},
509
- date = {2025-11-24},
508
+ version = {2.2.3},
509
+ date = {2025-12-4},
510
510
  }
511
511
  ```
512
512
 
@@ -18,7 +18,7 @@ classifiers = [
18
18
  description = "System resilience modelling, simulation, and assessment in Python"
19
19
  license = {file="LICENSE"}
20
20
  name = "fmdtools"
21
- version="2.2.2"
21
+ version="2.2.3"
22
22
  readme = "README.md"
23
23
  requires-python = ">=3.11"
24
24
 
File without changes
File without changes
File without changes
File without changes