engforge 0.3.0__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.
Files changed (69) hide show
  1. engforge/__init__.py +26 -0
  2. engforge/_testing_components.py +864 -0
  3. engforge/analysis.py +257 -0
  4. engforge/attr_dynamics.py +222 -0
  5. engforge/attr_plotting.py +811 -0
  6. engforge/attr_signals.py +116 -0
  7. engforge/attr_slots.py +211 -0
  8. engforge/attr_solver.py +443 -0
  9. engforge/attributes.py +326 -0
  10. engforge/common.py +76 -0
  11. engforge/component_collections.py +276 -0
  12. engforge/components.py +55 -0
  13. engforge/configuration.py +673 -0
  14. engforge/dataframe.py +185 -0
  15. engforge/datastores/__init__.py +116 -0
  16. engforge/datastores/data.py +519 -0
  17. engforge/datastores/gdocs.py +2650 -0
  18. engforge/datastores/reporting.py +935 -0
  19. engforge/datastores/secrets.py +549 -0
  20. engforge/dynamics.py +662 -0
  21. engforge/eng/__init__.py +0 -0
  22. engforge/eng/costs.py +1459 -0
  23. engforge/eng/fluid_material.py +279 -0
  24. engforge/eng/geometry.py +1060 -0
  25. engforge/eng/pipes.py +1211 -0
  26. engforge/eng/prediction.py +366 -0
  27. engforge/eng/solid_materials.py +445 -0
  28. engforge/eng/structure.py +2228 -0
  29. engforge/eng/structure_beams.py +821 -0
  30. engforge/eng/thermodynamics.py +271 -0
  31. engforge/engforge_attributes.py +380 -0
  32. engforge/env_var.py +214 -0
  33. engforge/locations.py +18 -0
  34. engforge/logging.py +290 -0
  35. engforge/patterns.py +449 -0
  36. engforge/problem_context.py +2251 -0
  37. engforge/properties.py +501 -0
  38. engforge/reporting.py +595 -0
  39. engforge/solveable.py +964 -0
  40. engforge/solver.py +316 -0
  41. engforge/solver_utils.py +726 -0
  42. engforge/system.py +143 -0
  43. engforge/system_reference.py +299 -0
  44. engforge/tabulation.py +288 -0
  45. engforge/test/__init__.py +2 -0
  46. engforge/test/_pre_test_structures.py +221 -0
  47. engforge/test/report_testing.py +158 -0
  48. engforge/test/test_airfilter.py +91 -0
  49. engforge/test/test_analysis.py +3 -0
  50. engforge/test/test_comp_iter.py +196 -0
  51. engforge/test/test_composition.py +139 -0
  52. engforge/test/test_costs.py +338 -0
  53. engforge/test/test_dynamics.py +109 -0
  54. engforge/test/test_dynamics_spaces.py +39 -0
  55. engforge/test/test_four_bar.py +97 -0
  56. engforge/test/test_modules.py +72 -0
  57. engforge/test/test_performance.py +93 -0
  58. engforge/test/test_pipes.py +49 -0
  59. engforge/test/test_problem.py +411 -0
  60. engforge/test/test_problem_deepscoping.py +68 -0
  61. engforge/test/test_slider_crank.py +51 -0
  62. engforge/test/test_solver.py +343 -0
  63. engforge/test/test_tabulation.py +233 -0
  64. engforge/typing.py +92 -0
  65. engforge-0.3.0.dist-info/METADATA +392 -0
  66. engforge-0.3.0.dist-info/RECORD +69 -0
  67. engforge-0.3.0.dist-info/WHEEL +5 -0
  68. engforge-0.3.0.dist-info/licenses/LICENSE +7 -0
  69. engforge-0.3.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,116 @@
1
+ """This module defines the slot attrs attribute to define the update behavior of a component or between components in an analysis"""
2
+
3
+ """Signals define data flow in the solver system. These updates can happen before or after a solver execution as defined in SolverMixin
4
+
5
+ """
6
+
7
+
8
+ import attrs, uuid
9
+ from engforge.attributes import ATTR_BASE, AttributeInstance, DEFAULT_COMBO
10
+ from engforge.attr_slots import SLOT_TYPES
11
+
12
+ VALID_MODES = ["pre", "post", "both"]
13
+
14
+
15
+ class SignalInstance(AttributeInstance):
16
+ """A decoupled signal instance to perform operations on a system instance"""
17
+
18
+ system: "System"
19
+ signal: "Signal"
20
+
21
+ # compiled info
22
+ target: "Ref"
23
+ source: "Ref"
24
+ class_attr: "Signal"
25
+
26
+ def __init__(self, signal, system) -> None:
27
+ self.class_attr = self.signal = signal
28
+ self.system = system
29
+ self.compile()
30
+
31
+ def compile(self, **kwargs):
32
+ self.source = self.system.locate_ref(self.signal.source)
33
+ self.target = self.system.locate_ref(self.signal.target)
34
+ self.system.debug(f"SIGNAL| setting {self.target} with {self.source}")
35
+
36
+ def apply(self):
37
+ """sets `target` from `source`"""
38
+ val = self.source.value()
39
+ if self.system.log_level < 10:
40
+ self.system.msg(f"Signal| applying {self.source}|{val} to {self.target}")
41
+ self.target.set_value(val)
42
+
43
+ @property
44
+ def mode(self) -> str:
45
+ return self.signal.mode
46
+
47
+ def as_ref_dict(self) -> dict:
48
+ return dict(
49
+ target=self.target,
50
+ source=self.source,
51
+ signal=self,
52
+ )
53
+
54
+ def get_alias(self, path):
55
+ return path.split(".")[-1]
56
+
57
+
58
+ class Signal(ATTR_BASE):
59
+ """A base class that handles initalization in the attrs meta class scheme by ultimately createing a SignalInstance"""
60
+
61
+ name: str
62
+ mode: str
63
+ target: str
64
+ source: str
65
+ config_cls: "System"
66
+ attr_prefix = "SIGNAL"
67
+ instance_class = SignalInstance
68
+
69
+ @classmethod
70
+ def define(cls, target: str, source: str, mode="pre", **kw):
71
+ """taking a component or system class as possible input valid input is later validated as an instance of that class or subclass"""
72
+ assert mode in VALID_MODES, f"invalid mode: {mode}"
73
+
74
+ active = kw.get("active", True)
75
+ combo_dflt = "default,signals"
76
+ combos = kw.get("combos", None)
77
+
78
+ # Create A New Signals Class
79
+ new_name = f"Signal_{mode}_{source}_to_{target}".replace(".", "_")
80
+ new_name = new_name + "_" + str(uuid.uuid4()).replace("-", "")[0:16]
81
+ new_dict = dict(
82
+ name=new_name,
83
+ mode=mode,
84
+ target=target,
85
+ source=source,
86
+ active=active,
87
+ combos=cls.process_combos(combos, combo_dflt, combo_dflt),
88
+ default_options=cls.default_options.copy(),
89
+ )
90
+ new_slot = type(new_name, (Signal,), new_dict)
91
+ new_slot.default_options["default"] = new_slot.make_factory()
92
+ new_slot.default_options["validator"] = new_slot.configure_instance
93
+ new_sig = cls._setup_cls(new_name, new_dict)
94
+ return new_sig
95
+
96
+ # FIXME: move to
97
+ @classmethod
98
+ def class_validate(cls, instance, **kwargs):
99
+ from engforge.properties import system_property
100
+
101
+ system = cls.config_cls
102
+
103
+ var_type = system.locate(cls.target)
104
+ if var_type is None:
105
+ raise Exception(f"target not found: {cls.target}")
106
+ assert isinstance(
107
+ var_type, attrs.Attribute
108
+ ), f"bad var {cls.target} not attribute: {var_type}"
109
+
110
+ driv_type = system.locate(cls.source)
111
+ if driv_type is None:
112
+ raise Exception(f"source not found: {cls.source}")
113
+
114
+
115
+ # Support Previous API
116
+ Signal = Signal
engforge/attr_slots.py ADDED
@@ -0,0 +1,211 @@
1
+ """This module defines the slot attrs attribute to ensure the type of component added is correct and to define behavior,defaults and argument passing behavio"""
2
+
3
+ import attrs
4
+ import uuid
5
+ from engforge.attributes import ATTR_BASE, AttributeInstance
6
+ import typing as typ
7
+ from engforge.logging import LoggingMixin
8
+
9
+ SLOT_TYPES = typ.Union["Component", "System"]
10
+ ITERATOR = typ.Union["ComponentIterator"]
11
+
12
+
13
+ class SlotLog(LoggingMixin):
14
+ pass
15
+
16
+
17
+ log = SlotLog()
18
+
19
+
20
+ class Slot(ATTR_BASE):
21
+ """Slot defines a way to accept different components or systems in a system"""
22
+
23
+ # These are added on System signals_slots_handler aka attrs field_transformer
24
+ name: str
25
+ accepted: SLOT_TYPES
26
+ config_cls: "System"
27
+ attr_prefix = "Slot"
28
+ none_ok: bool
29
+ instance_class = None # add the component or system on define
30
+
31
+ dflt_kw: dict = None # a dictionary of input in factory for custom inits
32
+ default_ok = True # otherwise accept class with defaults
33
+
34
+ is_iter: bool
35
+ wide: bool # only for component iterator
36
+ # default_options = ATTR_BASE.default_options.copy()
37
+ default_options = dict(
38
+ repr=True,
39
+ validator=None,
40
+ cmp=None,
41
+ hash=None,
42
+ init=True,
43
+ metadata=None,
44
+ converter=None,
45
+ kw_only=True,
46
+ eq=None,
47
+ order=None,
48
+ on_setattr=None,
49
+ inherited=False,
50
+ )
51
+
52
+ @classmethod
53
+ def define(
54
+ cls,
55
+ *component_or_systems: SLOT_TYPES,
56
+ none_ok=False,
57
+ default_ok=True,
58
+ dflt_kw: dict = None,
59
+ ):
60
+ """taking a component or system class as possible input valid input is later validated as an instance of that class or subclass
61
+
62
+ :param none_ok: will allow no component on that item, oterwise will fail
63
+ :param default_ok: will create the slot class with no input if true
64
+ :param dflt_kw: a dictionary of input in factory for custom inits overrides defaults_ok
65
+ #TODO: add default_args,default_kwargs
66
+ """
67
+ from engforge.components import Component
68
+ from engforge.component_collections import ComponentIter
69
+ from engforge.system import System
70
+ from engforge.eng.costs import CostModel
71
+
72
+ # Format THe Accepted Component Types
73
+ assert (
74
+ len(component_or_systems) == 1
75
+ ), "only one slot allowed, try making a subclass"
76
+ assert not any(
77
+ [issubclass(c, ComponentIter) for c in component_or_systems]
78
+ ), f"`ComponentIter` slot should be defined with `define_iterator` "
79
+ assert all(
80
+ [
81
+ issubclass(c, Component) or issubclass(c, System)
82
+ for c in component_or_systems
83
+ ]
84
+ ), "Not System Or Component Input"
85
+
86
+ ### Cost models should always default to None
87
+ # FIXME: is this nessicary?
88
+ if any([issubclass(c, CostModel) for c in component_or_systems]):
89
+ default_ok = False
90
+ none_ok = True
91
+
92
+ new_dict = dict(
93
+ accepted=component_or_systems,
94
+ none_ok=none_ok,
95
+ default_ok=default_ok,
96
+ dflt_kw=dflt_kw,
97
+ instance_class=component_or_systems[0],
98
+ default_options=cls.default_options.copy(),
99
+ )
100
+
101
+ # slot
102
+ new_name = cls.attr_prefix + "_"
103
+ new_sig = cls._setup_cls(new_name, new_dict)
104
+ return new_sig
105
+
106
+ @classmethod
107
+ def define_iterator(
108
+ cls,
109
+ *component_or_systems: ITERATOR,
110
+ none_ok: bool = False,
111
+ default_ok: bool = True,
112
+ wide: bool = True,
113
+ dflt_kw: dict = None,
114
+ ):
115
+ """taking a type of component iterator, defines an interface that can be 'wide' where all items are executed in the same row on `System.run()`.
116
+
117
+ Conversely if `wide` is false the system will loop over each item as if it was included in System.run(). Multiple ComponentIterators with wide=False will result in a `outer join` of the items.
118
+
119
+ :param none_ok: will allow no component on that item, otherwise will fail
120
+ :param default_ok: will create the slot class with no input if true
121
+ :param dflt_kw: a dictionary of input in factory for custom inits
122
+ :param wide: default is true, will determine if wide dataframe format, or outerproduct format when `System.run()` is called
123
+ """
124
+ from engforge.components import Component
125
+ from engforge.component_collections import ComponentIter
126
+ from engforge.system import System
127
+
128
+ # Format THe Accepted Component Types
129
+ assert (
130
+ len(component_or_systems) == 1
131
+ ), "only one slot allowed, try making a subclass"
132
+ assert all(
133
+ [issubclass(c, ComponentIter) for c in component_or_systems]
134
+ ), "Not System Or Component Input"
135
+
136
+ # FIXME: come up with a better name :)
137
+ new_name = f"SlotITER_{str(uuid.uuid4()).replace('-','')[0:16]}"
138
+ new_slot = type(
139
+ new_name,
140
+ (Slot,),
141
+ dict(
142
+ # default=cls.make_factory(),
143
+ name=new_name,
144
+ accepted=component_or_systems,
145
+ none_ok=none_ok,
146
+ default_ok=default_ok,
147
+ dflt_kw=dflt_kw,
148
+ is_iter=True,
149
+ wide=wide,
150
+ instance_class=component_or_systems[0],
151
+ default_options=cls.default_options.copy(),
152
+ ),
153
+ )
154
+ new_slot.default_options["validator"] = new_slot.configure_instance
155
+ new_slot.default_options["default"] = new_slot.make_factory()
156
+ return new_slot
157
+
158
+ # Create a validator function
159
+ @classmethod
160
+ def configure_instance(cls, instance, attribute, value):
161
+ from engforge.component_collections import ComponentIter
162
+
163
+ comp_cls = cls.config_cls
164
+
165
+ # apply wide behavior to componentiter instance
166
+ if isinstance(value, ComponentIter) and attribute.type.wide == False:
167
+ # print(f'validate {instance} {attribute} {value}')
168
+ value.wide = False
169
+
170
+ if value in cls.accepted or all([value is None, cls.none_ok]):
171
+ return True
172
+
173
+ if any([isinstance(value, a) for a in cls.accepted]):
174
+ return True
175
+
176
+ if cls.default_ok and value is None:
177
+ return True
178
+
179
+ raise ValueError(
180
+ f"{instance} value {value} is not an accepted type for slot: {comp_cls.__name__}.{cls.name}"
181
+ )
182
+
183
+ @classmethod
184
+ def make_factory(cls, **kwargs):
185
+ accepted = cls.accepted
186
+ log.debug(f"slot instance factory: {cls} {accepted}, {kwargs}")
187
+
188
+ if isinstance(accepted, (tuple, list)) and len(accepted) > 0:
189
+ accepted = accepted[0]
190
+
191
+ log.debug(f"slot factory: {accepted},{cls.dflt_kw},{cls.default_ok}")
192
+ if cls.dflt_kw:
193
+ return attrs.Factory(cls.make_accepted(accepted, **cls.dflt_kw), False)
194
+ elif cls.default_ok:
195
+ return attrs.Factory(accepted, False)
196
+ else:
197
+ return None
198
+
199
+ @classmethod
200
+ def make_accepted(cls, accepted, **kw):
201
+ log.debug(f"making accepted: {cls.instance_class} {kw}")
202
+ return lambda: accepted(**kw)
203
+
204
+ @classmethod
205
+ def handle_instance(cls, canidate):
206
+ """a passthrough"""
207
+ return canidate
208
+
209
+
210
+ # Support Previous SnakeCase
211
+ Slot = Slot