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
engforge/properties.py ADDED
@@ -0,0 +1,501 @@
1
+ """
2
+ Like typical python properties, normal functions are embelished with additional functionality.
3
+
4
+ `system_properties` is a core function that adds meta information to a normal python property to include its output in the results. It is the "y" in y=f(x).
5
+
6
+ `class_cache` a subclassing safe property that stores the result at runtime
7
+
8
+ `solver_cache` a property that is recalculated any time there is an update to any attrs variable.
9
+ """
10
+
11
+ import os
12
+ from engforge.logging import LoggingMixin
13
+ from engforge.typing import TABLE_TYPES
14
+
15
+ IS_BUILD = os.environ.get("SPHINX_BUILD", "false").strip().lower() == "true"
16
+
17
+
18
+ class PropertyLog(LoggingMixin):
19
+ pass
20
+
21
+
22
+ log = PropertyLog()
23
+
24
+ _basic_valild_types = (int, str, float) # check for return_type
25
+
26
+
27
+ class engforge_prop:
28
+ """
29
+ an interface for extension and identification and class return support
30
+
31
+ Use as follows:
32
+ @engforge_prop
33
+ def our_custom_function(self) -> return_type:
34
+ pass
35
+ """
36
+
37
+ valid_types = _basic_valild_types
38
+ must_return = False
39
+
40
+ def __init__(self, fget=None, fset=None, fdel=None, *args, **kwargs):
41
+ """call with the function you want to wrap in a decorator"""
42
+ self.valild_types = (int, str, float) # check for return_type
43
+ self.fget = fget
44
+ if fget:
45
+ self.gname = fget.__name__
46
+ self.get_func_return(fget)
47
+ self.fset = fset
48
+ self.fdel = fdel
49
+
50
+ def __call__(self, fget=None, fset=None, fdel=None, doc=None, *args, **kwargs):
51
+ """this will be called when input is provided before property is set"""
52
+ if fget and self.fget is None:
53
+ self.gname = fget.__name__
54
+ self.get_func_return(fget)
55
+ self.fget = fget
56
+
57
+ if self.fset is None:
58
+ self.fset = fset
59
+
60
+ if self.fdel is None:
61
+ self.fdel = fdel
62
+
63
+ return self
64
+
65
+ def get_func_return(self, func):
66
+ """ensures that the function has a return annotation, and that return annotation is in valid sort types"""
67
+ anno = func.__annotations__
68
+ typ = anno.get("return", None)
69
+ if not typ in self.valid_types and self.must_return:
70
+ raise Exception(
71
+ f"system_property input: function {func.__name__} must have valid return annotation of type: {(int,str,float)}"
72
+ )
73
+ else:
74
+ self.return_type = typ
75
+
76
+ def __get__(self, obj, objtype=None):
77
+ if obj is None:
78
+ return self # class support
79
+ if self.fget is None:
80
+ raise AttributeError("unreadable attribute")
81
+ return self.fget(obj)
82
+
83
+ def __set__(self, obj, value):
84
+ if self.fset is None:
85
+ raise AttributeError("can't set attribute")
86
+ self.fset(obj, value)
87
+
88
+ def __delete__(self, obj):
89
+ if self.fdel is None:
90
+ raise AttributeError("can't delete attribute")
91
+ self.fdel(obj)
92
+
93
+ def getter(self, fget):
94
+ return type(self)(fget, self.fset, self.fdel, self.__doc__)
95
+
96
+ def setter(self, fset):
97
+ return type(self)(self.fget, fset, self.fdel, self.__doc__)
98
+
99
+ def deleter(self, fdel):
100
+ return type(self)(self.fget, self.fset, fdel, self.__doc__)
101
+
102
+
103
+ class cache_prop(engforge_prop):
104
+ allow_set: bool = False # keep this flag false to maintain current persistent value
105
+
106
+ def __init__(self, *args, **kwargs):
107
+ self.allow_set = True
108
+ self.valild_types = (int, str, float) # check for return_type
109
+ super().__init__(*args, **kwargs)
110
+
111
+ def __set__(self, instance, value):
112
+ if self.allow_set:
113
+ self.set_cache(instance, reason="change", val=value)
114
+ else:
115
+ raise Exception(f"cannot set {self.gname}")
116
+
117
+ def set_cache(self, instance, reason="update", val=None):
118
+ raise NotImplementedError(
119
+ "cache_prop must be subclassed and set_cache method defined"
120
+ )
121
+
122
+
123
+ class system_property(engforge_prop):
124
+ """
125
+ this property notifies the system this is a property to be tabulated in the dataframe output.
126
+
127
+ @system_property
128
+ def function(...): < this uses __init__ to assign function
129
+
130
+ @system_property(desc='really nice',label='funky function')
131
+ def function(...): < this uses __call__ to assign function
132
+
133
+ When the underlying data has some random element, set stochastic=True and this will flag the component to always save data.
134
+
135
+ Functions wrapped with table type must have an return annotion of (int,float,str)... ex def func() -> int:
136
+ """
137
+
138
+ desc = ""
139
+ label = None
140
+ stochastic = False
141
+ get_func_return: type = None
142
+ return_type = None
143
+ must_return = True
144
+
145
+ def __init__(
146
+ self,
147
+ fget=None,
148
+ fset=None,
149
+ fdel=None,
150
+ doc=None,
151
+ desc=None,
152
+ label=None,
153
+ stochastic=False,
154
+ ):
155
+ """You can initalize just the functions, or precreate the object but with meta
156
+ @system_property
157
+ def function(...): < this uses __init__ to assign function
158
+
159
+ @system_property(desc='really nice',label='funky function')
160
+ def function(...): < this uses __call__ to assign function
161
+ """
162
+ self.valild_types = (int, str, float) # check for return_type
163
+ self.fget = fget
164
+ if fget:
165
+ self.get_func_return(fget)
166
+
167
+ self.fset = fset
168
+ self.fdel = fdel
169
+ if doc is None and fget is not None:
170
+ doc = fget.__doc__
171
+ elif doc is not None:
172
+ self.__doc__ = doc
173
+
174
+ if desc is not None:
175
+ self.desc = desc
176
+
177
+ if label is not None:
178
+ self.label = label
179
+ elif fget is not None:
180
+ self.label = fget.__name__.lower()
181
+
182
+ # Set random flag
183
+ self.stochastic = stochastic
184
+
185
+ def __call__(self, fget=None, fset=None, fdel=None, doc=None):
186
+ """this will be called when input is provided before property is set"""
187
+ if fget and self.fget is None:
188
+ self.get_func_return(fget)
189
+ self.fget = fget
190
+ if self.fset is None:
191
+ self.fset = fset
192
+ if self.fdel is None:
193
+ self.fdel = fdel
194
+
195
+ if doc is None and fget is not None:
196
+ doc = fget.__doc__
197
+ elif doc is not None:
198
+ self.__doc__ = doc
199
+
200
+ if self.label is None and fget is not None:
201
+ self.label = fget.__name__.lower()
202
+
203
+ return self
204
+
205
+ def get_func_return(self, func):
206
+ """ensures that the function has a return annotation, and that return annotation is in valid sort types"""
207
+ anno = func.__annotations__
208
+ typ = anno.get("return", None)
209
+ if not typ in (int, str, float):
210
+ raise Exception(
211
+ f"system_property input: function {func.__name__} must have valid return annotation of type: {(int,str,float)}"
212
+ )
213
+ else:
214
+ self.return_type = typ
215
+
216
+ def __get__(self, obj, objtype=None):
217
+ if obj is None:
218
+ return self # class support
219
+ if self.fget is None:
220
+ raise AttributeError("unreadable attribute")
221
+ return self.fget(obj)
222
+
223
+ def __set__(self, obj, value):
224
+ if self.fset is None:
225
+ raise AttributeError("can't set attribute")
226
+ self.fset(obj, value)
227
+
228
+ def __delete__(self, obj):
229
+ if self.fdel is None:
230
+ raise AttributeError("can't delete attribute")
231
+ self.fdel(obj)
232
+
233
+ def getter(self, fget):
234
+ return type(self)(fget, self.fset, self.fdel, self.__doc__)
235
+
236
+ def setter(self, fset):
237
+ return type(self)(self.fget, fset, self.fdel, self.__doc__)
238
+
239
+ def deleter(self, fdel):
240
+ return type(self)(self.fget, self.fset, fdel, self.__doc__)
241
+
242
+
243
+ # aliases
244
+ sys_prop = system_property
245
+ system_prop = system_property
246
+
247
+
248
+ class cached_system_property(system_property):
249
+ """A system property that caches the result when nothing changes. Use for expensive functions since the checking adds some overhead"""
250
+
251
+ gname: str
252
+
253
+ def get_func_return(self, func):
254
+ """ensures that the function has a return annotation, and that return annotation is in valid sort types"""
255
+ anno = func.__annotations__
256
+ typ = anno.get("return", None)
257
+ if not typ in (int, str, float):
258
+ raise Exception(
259
+ f"system_property input: function {func.__name__} must have valid return annotation of type: {(int,str,float)}"
260
+ )
261
+ else:
262
+ self.return_type = typ
263
+
264
+ @property
265
+ def private_var(self):
266
+ return f"_{self.gname}"
267
+
268
+ def __get__(self, instance: "TabulationMixin", objtype=None):
269
+ if instance is None:
270
+ return self
271
+ if not hasattr(instance, self.private_var) and not IS_BUILD:
272
+ from engforge.tabulation import TabulationMixin
273
+
274
+ assert issubclass(
275
+ instance.__class__, TabulationMixin
276
+ ), f"incorrect class: {instance.__class__.__name__}"
277
+ return self.set_cache(instance, reason="set")
278
+ elif instance.anything_changed:
279
+ return self.set_cache(instance)
280
+ return getattr(instance, self.private_var)
281
+
282
+ def set_cache(self, instance, reason="update", val=None):
283
+ if log.log_level < 5:
284
+ log.msg(
285
+ f"solver cache for {instance.identity}.{self.private_var}| {reason}"
286
+ )
287
+ if val is None:
288
+ val = self.fget(instance)
289
+ setattr(instance, self.private_var, val)
290
+ return val
291
+
292
+
293
+ # aliases
294
+ cached_sys_prop = system_property
295
+ cached_system_prop = system_property
296
+
297
+
298
+ # TODO: install solver reset / declarative instance cache+
299
+ class solver_cached(cache_prop):
300
+ """
301
+ A property that updates a first time and then anytime time the input data changed, as signaled by attrs.on_setattr callback
302
+ """
303
+
304
+ @property
305
+ def private_var(self):
306
+ return f"_{self.gname}"
307
+
308
+ def __get__(self, instance: "TabulationMixin", objtype=None):
309
+ if not hasattr(instance, self.private_var) and not IS_BUILD:
310
+ from engforge.tabulation import TabulationMixin
311
+
312
+ assert instance.__class__ is None or issubclass(
313
+ instance.__class__, TabulationMixin
314
+ ), f"incorrect class: {instance.__class__.__name__}"
315
+ return self.set_cache(instance, reason="set")
316
+
317
+ elif instance.anything_changed:
318
+ return self.set_cache(instance)
319
+
320
+ return getattr(instance, self.private_var)
321
+
322
+ def set_cache(self, instance, reason="update", val=None):
323
+ if log.log_level < 5:
324
+ log.debug(
325
+ f"caching attr for {instance.identity}.{self.private_var}| {reason}"
326
+ )
327
+ if val is None:
328
+ val = self.fget(instance) # default!
329
+ setattr(instance, self.private_var, val)
330
+ return val
331
+
332
+
333
+ # TODO: install solver reset / declarative instance cache+
334
+ class instance_cached(cache_prop):
335
+ """
336
+ A property that caches a result to an instance the first call then returns that each successive call
337
+ """
338
+
339
+ @property
340
+ def private_var(self):
341
+ return f"_{self.gname}"
342
+
343
+ def __get__(self, instance: "TabulationMixin", objtype=None):
344
+ if not hasattr(instance, self.private_var):
345
+ from engforge.tabulation import TabulationMixin
346
+
347
+ if instance.__class__ is not None and not IS_BUILD: # its an instance
348
+ assert issubclass(
349
+ instance.__class__, TabulationMixin
350
+ ), f"incorrect class: {instance.__class__.__name__}"
351
+ return self.set_cache(instance, reason="set")
352
+ else:
353
+ return self.fget(instance)
354
+ return getattr(instance, self.private_var)
355
+
356
+ def set_cache(self, instance, reason="update", val=None):
357
+ if log.log_level < 5:
358
+ log.debug(
359
+ f"caching instance for {instance.identity}.{self.private_var}| {reason}"
360
+ )
361
+ if val is None:
362
+ val = self.fget(instance) # default!
363
+ setattr(instance, self.private_var, val)
364
+ return val
365
+
366
+
367
+ class class_cache(cache_prop):
368
+ """a property that caches a value on the class at runtime, and then maintains that value for the duration of the program. A flag with the class name ensures the class is correct. Intended for instance methods"""
369
+
370
+ @property
371
+ def private_var(self):
372
+ return f"_{self.gname}"
373
+
374
+ @property
375
+ def id_var(self):
376
+ return f"__{self.gname}"
377
+
378
+ def __get__(self, instance: "Any", objtype=None):
379
+ cls = instance.__class__
380
+ if hasattr(cls, self.private_var):
381
+ # check and gtfo
382
+ # print(f'getting private var {self.private_var}')
383
+ return getattr(cls, self.private_var)
384
+ else:
385
+ if not hasattr(cls, self.id_var):
386
+ # initial cache
387
+ return self.set_cache(instance, cls)
388
+ elif cls.__name__ != getattr(cls, self.id_var):
389
+ # recache the system if name doesn't match
390
+ return self.set_cache(instance, cls)
391
+ # print(f'getting private def {self.private_var}')
392
+
393
+ return getattr(cls, self.private_var)
394
+
395
+ def set_cache(self, instance, cls):
396
+ log.debug(f"cls caching for {cls.__name__}.{self.private_var}")
397
+ val = self.fget(instance)
398
+ setattr(cls, self.private_var, val)
399
+ setattr(cls, self.id_var, cls.__name__)
400
+ return val
401
+
402
+
403
+ # #TODO: make a `solver_context` that exposes a ray python remote funciton with wait & other provisions... add to a call graph and optimize later
404
+ # NOTE: challenge to handle class/instance/functions with same code
405
+ # from engforge.properties import engforge_prop
406
+ # class solver_context(engforge_prop):
407
+ #
408
+ # fget = None
409
+ #
410
+ # def __init__(
411
+ # self,
412
+ # fget=None,
413
+ # **kwargs,
414
+ # ):
415
+ # if fget:
416
+ # self.fget = remote_func_ret(fget)
417
+ # self.ray_kw = kwargs
418
+ #
419
+ # def __call__(self, fget=None, fset=None, fdel=None, doc=None):
420
+ # """this will be called when input is provided before property is set"""
421
+ # if fget and self.fget is None:
422
+ # self.fget = remote_func_ret(fget,**self.ray_kw)
423
+ #
424
+ # return self
425
+ #
426
+ # def __get__(self, obj, objtype=None):
427
+ # if obj is None:
428
+ # return self
429
+ # if self.fget is None:
430
+ # raise AttributeError("unreadable attribute")
431
+ # return self.fget(obj)
432
+ #
433
+ #
434
+ # class remote_func_ret:
435
+ # fget = None
436
+ # remote_fget = None
437
+ # def __init__(self,func=None,**kwargs):
438
+ # self.ray_kwargs = kwargs
439
+ # if func:
440
+ # self.setup_func(func)
441
+ #
442
+ # def setup_func(self,func):
443
+ # self.fget = func
444
+ # if self.ray_kwargs:
445
+ # d = ray.remote(**self.ray_kwargs)
446
+ # self.remote_fget = d(func)
447
+ # else:
448
+ # self.remote_fget = ray.remote(func)
449
+ #
450
+ # def __call__(self,*args,**kwargs):
451
+ # return self.fget(*args,**kwargs)
452
+ #
453
+ # def remote(self,wait=False,getret=False,*args,**kwargs):
454
+ # res = self.remote_fget.remote(*args,**kwargs)
455
+ #
456
+ # if wait:
457
+ # ray.wait([res])
458
+ # return res
459
+ # if getret:
460
+ # return ray.get([res])
461
+ # return res
462
+ #
463
+ #
464
+ # @solver_context
465
+ # def test_deck(a,b,c):
466
+ # print(a,b,c)
467
+ #
468
+ # return a * b * c
469
+ #
470
+ # @solver_context(num_cpus=2)
471
+ # def super_test(a,b,c):
472
+ # print(a,b,c)
473
+ #
474
+ # return a * b * c
475
+ #
476
+ #
477
+ # class nonactor:
478
+ #
479
+ # def __init__(self,c):
480
+ # self.c = c
481
+ #
482
+ # @solver_context
483
+ # def test_inst(self,a,b):
484
+ # print(a,b)
485
+ #
486
+ # return a * b * self.c
487
+ #
488
+ # @solver_context(num_cpus=2)
489
+ # def inst_test(self,a,b):
490
+ # print(a,b)
491
+ #
492
+ # return a * b * self.c
493
+ #
494
+ # print(test_deck)
495
+ # print(super_test)
496
+ # print(nonactor.test_inst)
497
+ # print(nonactor.inst_test)
498
+ #
499
+ # ac = nonactor(1)
500
+ # print(ac.test_inst)
501
+ # print(ac.inst_test)