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,343 @@
1
+ import unittest
2
+ from engforge._testing_components import *
3
+ from engforge.problem_context import ProblemExec
4
+ import itertools
5
+
6
+
7
+ indep = "*x,*y,*z"
8
+ indep_l = indep.split(",")
9
+
10
+
11
+ class SolverRefSelection(unittest.TestCase):
12
+ def setUp(self) -> None:
13
+ self.sc = CubeSystem()
14
+
15
+ def test_comps_solver_act(self):
16
+ inv = "ineq_cost,ineq_length,min_len" + "," + indep
17
+ acts = ["*costA", "*lenA"]
18
+ for act in [True, False]:
19
+ for cmb in [inv, inv.split(",")]:
20
+ extra = dict(
21
+ combos=cmb,
22
+ slv_vars="*",
23
+ activate=acts if act else None,
24
+ only_active=act,
25
+ )
26
+ info = self.sc.solver_vars(**extra)["attrs"]
27
+ ans = {"x", "y", "z", "comp.x", "comp.y", "comp.z"}
28
+ self.assertEqual(set(info["solver.var"]), ans)
29
+ self.assertEqual(set(info["dynamics.state"]), ans)
30
+ self.assertEqual(set(info["dynamics.rate"]), ans)
31
+
32
+ empt = ["solver.eq", "solver.obj", "time", "slot", "signal"]
33
+ for emp in empt:
34
+ self.assertEqual(info[emp], {})
35
+
36
+ if act:
37
+ ineq = {"comp.lenA", "comp.costA", "costA", "lenA"}
38
+ self.assertEqual(set(info["solver.ineq"]), ineq)
39
+
40
+ def test_comps_solver_obj(self):
41
+ inv = "obj_size,ineq_cost,ineq_length,min_len" + "," + indep
42
+ acts = ["*costA", "*lenA"]
43
+ aacts = ["*costA", "*lenA", "*size"]
44
+ for act in [True, False]:
45
+ for cmb in [inv, inv.split(",")]:
46
+ actv = acts if act else aacts
47
+ extra = dict(combos=cmb, slv_vars="*", activate=actv, only_active=True)
48
+ info = self.sc.solver_vars(**extra)["attrs"]
49
+ ans = {"x", "y", "z", "comp.x", "comp.y", "comp.z"}
50
+ self.assertEqual(set(info["solver.var"]), ans)
51
+ self.assertEqual(set(info["dynamics.state"]), ans)
52
+ self.assertEqual(set(info["dynamics.rate"]), ans)
53
+
54
+ ineq = {"comp.lenA", "comp.costA", "costA", "lenA"}
55
+ self.assertEqual(set(info["solver.ineq"]), ineq)
56
+
57
+ empt = ["solver.eq", "time", "slot", "signal"]
58
+ for emp in empt:
59
+ self.assertEqual(info[emp], {})
60
+
61
+ if not act:
62
+ obj = {"size", "comp.size"}
63
+ self.assertEqual(set(info["solver.obj"]), obj)
64
+ else:
65
+ self.assertEqual(set(info["solver.obj"]), set())
66
+
67
+ def test_sys_solver(self):
68
+ # test combos
69
+ inv = "volume,height,min_len,total_*" + "," + indep
70
+ for act in [True, False]:
71
+ for cmb in [inv, inv.split(",")]:
72
+ extra = dict(
73
+ combos=cmb,
74
+ slv_vars=indep + ",hght,obj,*sys*",
75
+ activate=[],
76
+ only_active=act,
77
+ )
78
+ info = self.sc.solver_vars(**extra)["attrs"]
79
+ ans = {"x", "y", "z", "comp.x", "comp.y", "comp.z"}
80
+ self.assertEqual(set(info["solver.var"]), ans)
81
+ self.assertEqual(set(info["dynamics.state"]), ans)
82
+ self.assertEqual(set(info["dynamics.rate"]), ans)
83
+
84
+ obj = {"hght", "obj"}
85
+ self.assertEqual(set(info["solver.obj"]), obj)
86
+ ineq = {"sys_budget", "sys_length"}
87
+ self.assertEqual(set(info["solver.ineq"]), ineq)
88
+
89
+ empt = ["solver.eq", "time", "slot", "signal"]
90
+ for emp in empt:
91
+ self.assertEqual(info[emp], {})
92
+
93
+ def test_xyz_var_only(self):
94
+ """only xyz via vars"""
95
+ inv = indep
96
+ for act in [True, False]:
97
+ for cmb in [inv, inv.split(",")]:
98
+ extra = dict(combos=None, slv_vars=cmb, only_active=act)
99
+ info = self.sc.solver_vars(**extra)["attrs"]
100
+ ans = {"x", "y", "z", "comp.x", "comp.y", "comp.z"}
101
+ self.assertEqual(set(info["solver.var"]), ans)
102
+ self.assertEqual(set(info["dynamics.state"]), ans)
103
+ self.assertEqual(set(info["dynamics.rate"]), ans)
104
+
105
+ empt = [
106
+ "solver.eq",
107
+ "solver.ineq",
108
+ "solver.obj",
109
+ "time",
110
+ "slot",
111
+ "signal",
112
+ ]
113
+ for emp in empt:
114
+ self.assertEqual(info[emp], {})
115
+
116
+ def test_xyz_combo_only(self):
117
+ """only xyz via combos"""
118
+ inv = "x,y,z"
119
+ for act in [True, False]:
120
+ for cmb in [inv, inv.split(",")]:
121
+ extra = dict(combos=None, slv_vars=indep, only_active=act)
122
+ info = self.sc.solver_vars(**extra)["attrs"]
123
+ ans = {"x", "y", "z", "comp.x", "comp.y", "comp.z"}
124
+ self.assertEqual(set(info["solver.var"]), ans)
125
+ self.assertEqual(set(info["dynamics.state"]), ans)
126
+ self.assertEqual(set(info["dynamics.rate"]), ans)
127
+
128
+ empt = [
129
+ "solver.eq",
130
+ "solver.ineq",
131
+ "solver.obj",
132
+ "time",
133
+ "slot",
134
+ "signal",
135
+ ]
136
+ for emp in empt:
137
+ self.assertEqual(info[emp], {})
138
+
139
+ def test_xyz_act_nocombo(self):
140
+ """activated aren't included because no combos are given"""
141
+ extra = dict(
142
+ combos=None,
143
+ slv_vars=indep,
144
+ activate=["*costA", "*lenA", "*size"],
145
+ only_active=True,
146
+ )
147
+
148
+ info = self.sc.solver_vars(**extra)["attrs"]
149
+ ans = {"x", "y", "z", "comp.x", "comp.y", "comp.z"}
150
+ self.assertEqual(set(info["solver.var"]), ans)
151
+ self.assertEqual(set(info["dynamics.state"]), ans)
152
+ self.assertEqual(set(info["dynamics.rate"]), ans)
153
+
154
+ empt = [
155
+ "solver.eq",
156
+ "solver.ineq",
157
+ "solver.obj",
158
+ "time",
159
+ "slot",
160
+ "signal",
161
+ ]
162
+ for emp in empt:
163
+ self.assertEqual(info[emp], {})
164
+
165
+
166
+ import pprint
167
+
168
+
169
+ class SingleCompSolverTest(unittest.TestCase):
170
+ inequality_min = -1e-6
171
+
172
+ def setUp(self) -> None:
173
+ self.sc = CubeSystem(comp=CubeComp())
174
+
175
+ def test_exec_results(self):
176
+ extra = dict(combos=indep_l, slv_vars=indep, activate=[], only_active=True)
177
+ with ProblemExec(self.sc, extra) as pb:
178
+ o = self.sc.execute(**extra)
179
+ self.assertDictEqual(o["Xstart"], o["Xans"])
180
+
181
+ def test_run_results(self):
182
+ """test that inputs stay the same when no objecives present"""
183
+ extra = dict(combos=indep, slv_vars=indep_l, activate=[], only_active=True)
184
+
185
+ scx, scy, scz = self.sc.x, self.sc.y, self.sc.z
186
+ sccx, sccy, sccz = self.sc.comp.x, self.sc.comp.y, self.sc.comp.z
187
+ vray = [scx, scy, scz, sccx, sccy, sccz]
188
+
189
+ o = self.sc.run(**extra)
190
+
191
+ # dfray = self.sc.dataframe[['x','y','z','comp_x','comp_y','comp_z']].iloc[0].values
192
+ # self.assertFalse(np.all(dfray==vray),msg=f'{dfray}\n{vray}')
193
+
194
+ scx, scy, scz = self.sc.x, self.sc.y, self.sc.z
195
+ sccx, sccy, sccz = self.sc.comp.x, self.sc.comp.y, self.sc.comp.z
196
+ v2ray = [scx, scy, scz, sccx, sccy, sccz]
197
+
198
+ self.assertTrue(np.all(vray == v2ray), msg="revert values changed!")
199
+
200
+ def test_comp_method_equivalence(self):
201
+ objs = {"obj_eff": ["eff", "effF"], "obj_size": ["size", "sizeF"]}
202
+ cons = {
203
+ "ineq_cost": ["costA", "costF", "costP"],
204
+ "ineq_length": ["lenA", "lenF", "lenP"],
205
+ }
206
+
207
+ for ok, ck in itertools.product(objs, cons):
208
+ attempts = []
209
+ for var in objs[ok]:
210
+ for con in cons[ck]:
211
+ extra = dict(
212
+ combos=[ok, ck, "min_len"],
213
+ slv_vars=indep,
214
+ activate=[var, con],
215
+ only_active=True,
216
+ both_match=False,
217
+ )
218
+ o = self.sc.execute(**extra)
219
+ attempts.append(o)
220
+
221
+ # check for equivalence
222
+ a0 = attempts[0]
223
+ for atmpt in attempts[1:]:
224
+ ak1 = a0["Xans"]
225
+ ak2 = atmpt["Xans"]
226
+ self.assertEqual(set(ak1), set(ak2))
227
+ for k in ak1:
228
+ self.assertAlmostEqual(ak1[k], ak2[k], places=4)
229
+
230
+ def test_system_execute(self):
231
+ """check the system solver methods work as expected (objectives are optimized)"""
232
+ extra = dict(
233
+ combos="volume,height,min_len,total_*",
234
+ slv_vars=indep,
235
+ activate=[],
236
+ only_active=True,
237
+ both_match=False,
238
+ )
239
+
240
+ H = self.sc.execute(**extra, weights={"hght": 1, "obj": 0})
241
+ V = self.sc.execute(**extra, weights={"hght": 0, "obj": 1})
242
+
243
+ self.assertGreater(H["Yobj"]["hght"], V["Yobj"]["hght"])
244
+ self.assertGreater(V["Yobj"]["obj"], H["Yobj"]["obj"])
245
+
246
+ def test_system_run(self):
247
+ """check the system solver methods work as expected (objectives are optimized)"""
248
+ extra = dict(
249
+ combos="volume,height,min_len,total_*",
250
+ slv_vars=indep,
251
+ activate=[],
252
+ only_active=True,
253
+ both_match=False,
254
+ )
255
+
256
+ H = self.sc.run(**extra, weights={"hght": 1, "obj": 0})["output"][0]
257
+ V = self.sc.run(**extra, weights={"hght": 0, "obj": 1})["output"][0]
258
+
259
+ self.assertGreater(H["Yobj"]["hght"], V["Yobj"]["hght"])
260
+ self.assertGreater(V["Yobj"]["obj"], H["Yobj"]["obj"])
261
+
262
+ def test_system_solver(self):
263
+ """check the system solver methods work as expected (objectives are optimized)"""
264
+ extra = dict(
265
+ combos="volume,height,min_len,total_*",
266
+ slv_vars=indep,
267
+ activate=[],
268
+ only_active=True,
269
+ both_match=False,
270
+ )
271
+
272
+ H = self.sc.solver(**extra, weights={"hght": 1, "obj": 0})
273
+ V = self.sc.solver(**extra, weights={"hght": 0, "obj": 1})
274
+
275
+ self.assertGreater(H["Yobj"]["hght"], V["Yobj"]["hght"])
276
+ self.assertGreater(V["Yobj"]["obj"], H["Yobj"]["obj"])
277
+
278
+ def test_system_eval(self):
279
+ """check the system solver methods work as expected (objectives are optimized)"""
280
+ extra = dict(
281
+ combos="volume,height,min_len,total_*",
282
+ slv_vars=indep,
283
+ activate=[],
284
+ only_active=True,
285
+ both_match=False,
286
+ )
287
+
288
+ H = self.sc.eval(**extra, weights={"hght": 1, "obj": 0})
289
+ V = self.sc.eval(**extra, weights={"hght": 0, "obj": 1})
290
+
291
+ self.assertGreater(H["Yobj"]["hght"], V["Yobj"]["hght"])
292
+ self.assertGreater(V["Yobj"]["obj"], H["Yobj"]["obj"])
293
+
294
+
295
+ # TODO: rewrite these tests
296
+ # def test_selective_exec(self):
297
+ # extra = dict(combos='*fun*,goal',ign_combos=['z_sym_eq','*size*','*eff*'])
298
+ # o = self.sc.execute(**extra)
299
+ # res = self.test_results(o)
300
+ # self.test_selection(o,res,extra)
301
+ #
302
+ # def test_runmtx(self):
303
+ # extra = dict(budget=[100,500,1000],max_length=[50,100,200])
304
+ # o = self.sc.run(**extra)
305
+ # res = self.test_results(o)
306
+ # df = self.test_dataframe(o)
307
+ # self.test_selection(o,res,extra)
308
+ #
309
+ # def test_selective_runmtx(self):
310
+ # extra = dict(budget=[100,500,1000],max_length=[50,100,200],combos='*fun*,goal',ign_combos=['z_sym_eq','*size*','*eff*'])
311
+ # o = self.sc.run(**extra)
312
+ # res = self.test_results(o)
313
+ # df = self.test_dataframe(o)
314
+ # self.test_selection(o,res,extra)
315
+ #
316
+ # def test_run_wildcard(self):
317
+ # extra = dict(combos='*fun*,goal',ign_combos='*',budget=[100,500,1000],max_length=[50,100,200])
318
+ # o = self.sc.run(**extra)
319
+ # res = self.test_results(o)
320
+ # df = self.test_dataframe(o)
321
+ # self.test_selection(o,res,extra)
322
+ #
323
+ # #Checks
324
+ # def test_dataframe(self,results)->dict:
325
+ # #print(results)
326
+ # print(NotImplementedError("#FIXME!"))
327
+ #
328
+ # def test_selection(self,results,df_res,extra):
329
+ # #print(results,df_res,extra)
330
+ # print(NotImplementedError("#FIXME!"))
331
+ #
332
+ # def test_results(self,results):
333
+ # Ycon = results['Ycon']
334
+ # costmg = {k:v for k,v in Ycon.items() if 'cost' in k}
335
+ # lenmg = {k:v for k,v in Ycon.items() if 'len' in k}
336
+ #
337
+ # #Test input equivalence of methods
338
+ # self.assertEqual(len(set(costmg.values())),1)
339
+ # self.assertEqual(len(set(lenmg.values())),1)
340
+ #
341
+ # #Test inequality constraints
342
+ # yvals = (v >= self.inequality_min for v in Ycon.values())
343
+ # self.assertTrue(all(yvals))
@@ -0,0 +1,233 @@
1
+ import unittest
2
+ import io
3
+ import tempfile
4
+
5
+ from engforge.configuration import forge
6
+ from engforge.tabulation import system_property
7
+ from engforge.components import Component
8
+ from engforge.system import System
9
+ from engforge.component_collections import ComponentIterator, ComponentDict
10
+ from engforge.problem_context import ProblemExec
11
+ import attr
12
+ import os
13
+ import numpy
14
+ import random
15
+
16
+ from pprint import pprint
17
+
18
+ # from engforge.logging import LoggingMixin, change_all_log_levels
19
+ # change_all_log_levels(10)
20
+
21
+
22
+ @forge
23
+ class TestConfig(System):
24
+ attrs_prop: float = attr.ib(1.0)
25
+ attrs_str: str = attr.ib("hey now")
26
+
27
+ @system_property
28
+ def test_one(self) -> int:
29
+ return 1
30
+
31
+ @system_property(stochastic=True)
32
+ def test_two(self) -> float:
33
+ return random.random()
34
+
35
+ @system_property(label="three", desc="some words")
36
+ def test_three(self) -> int:
37
+ return 2
38
+
39
+ @system_property(label="four", desc="would make sense", stochastic=True)
40
+ def test_four(self) -> float:
41
+ return random.random()
42
+
43
+
44
+ class Test(unittest.TestCase):
45
+ test_file_name = "test_dataframe_file"
46
+ test_dir = "~/"
47
+
48
+ def setUp(self):
49
+ self.test_config = TestConfig(name="testconfig")
50
+
51
+ # self.test_dir = tempfile.mkdtemp()
52
+ #
53
+ # def tearDown(self):
54
+ # contents_local = os.listdir(self.test_dir)
55
+ # for fil in contents_local:
56
+ # if self.test_file_name in fil:
57
+ # rfil = os.path.join(self.test_dir, fil)
58
+
59
+ def test_property_labels(self):
60
+ ans = set(["four", "test_two", "test_one", "three", "converged", "run_id"])
61
+ self.assertEqual(set(self.test_config.system_properties_labels), ans)
62
+
63
+ def test_property_types(self):
64
+ ans = [int, float, int, float, int, int]
65
+ self.assertEqual(self.test_config.system_properties_types, ans)
66
+
67
+ def test_property_desc(self):
68
+ ans = ["", "", "some words", "would make sense", "", ""]
69
+ self.assertEqual(self.test_config.system_properties_description, ans)
70
+
71
+ def test_table_vals(self):
72
+ vals_ans = (1, 2)
73
+ for val in vals_ans:
74
+ self.assertTrue(
75
+ any(
76
+ [
77
+ val == valcan if type(valcan) is int else False
78
+ for valcan in self.test_config.system_properties
79
+ ]
80
+ )
81
+ )
82
+
83
+ def test_assemble_data_always_save(self):
84
+ self.test_config.info(
85
+ f"testing data assembly {self.test_config.always_save_data}"
86
+ )
87
+ self.test_config._always_save_data = True
88
+ # Run before test_table_to...
89
+ self.assertFalse(self.test_config.dataframe.shape[0])
90
+ with ProblemExec(self.test_config, {}) as px:
91
+ px.save_data()
92
+
93
+ self.assertTrue(px.dataframe.shape[0])
94
+ self.assertTrue(1 == px.dataframe.shape[0])
95
+
96
+ # Stochastic Props so this will work without input change.
97
+ px.save_data()
98
+ self.assertTrue(2 == px.dataframe.shape[0])
99
+
100
+ def test_assemble_data_on_input(self):
101
+ # change this to check behavior to adjust stochastic behavior
102
+ # print(f"testing data assembly {self.test_config.always_save_data}")
103
+ # Run before test_table_to...
104
+ self.assertFalse(self.test_config.dataframe.shape[0])
105
+ self.test_config._always_save_data = False
106
+ self.test_config.info(
107
+ f"testing data assembly {self.test_config.always_save_data}"
108
+ )
109
+
110
+ with ProblemExec(self.test_config, {}) as px:
111
+ px.save_data()
112
+
113
+ # print(px.dataframe,len(px.dataframe))
114
+ self.assertTrue(px.dataframe.shape[0])
115
+ self.assertTrue(1 == px.dataframe.shape[0])
116
+
117
+ # No Update So This Wont Save
118
+ px.save_data()
119
+ self.assertFalse(2 == px.dataframe.shape[0])
120
+
121
+ # Change Something
122
+ cur_val = self.test_config.attrs_prop
123
+ new_val = 6 + cur_val
124
+ self.test_config.info(f"setting attrs prop on in {cur_val } => {new_val}")
125
+
126
+ self.test_config.attrs_prop = new_val
127
+ px.save_data()
128
+ self.assertTrue(2 == px.dataframe.shape[0])
129
+
130
+ def test_dataframe(self, iter=5):
131
+ attr_in = {}
132
+ cur_dict = self.test_config.data_dict
133
+ with ProblemExec(self.test_config, {}) as px:
134
+ for i in range(iter):
135
+ with ProblemExec(self.test_config, {}) as px:
136
+ cur_val = self.test_config.attrs_prop
137
+ attr_in[i] = val = cur_val + i**2.0
138
+ self.test_config.info(f"setting attrs prop df {cur_val } => {val}")
139
+ self.test_config.attrs_prop = val
140
+ px.save_data()
141
+ px.exit_with_state()
142
+ postdict = self.test_config.data_dict
143
+ self.assertDictEqual(cur_dict, postdict)
144
+ px.exit_with_state()
145
+
146
+ postdict = self.test_config.data_dict
147
+ # self.test_config.info(f'cur_dict {cur_dict} postdict {postdict}')
148
+ self.assertDictEqual(cur_dict, postdict)
149
+
150
+ df = self.test_config.dataframe
151
+ pprint(df)
152
+
153
+ self.assertEqual(len(df.index), iter)
154
+
155
+ self.assertEqual(df["test_one"].max(), 1)
156
+ self.assertEqual(df["test_one"].min(), 1)
157
+
158
+ self.assertEqual(df["test_three"].max(), 2)
159
+ self.assertEqual(df["test_three"].min(), 2)
160
+
161
+ l1 = list(df["attrs_prop"])
162
+ l2 = [attr_in[i] for i in range(iter)]
163
+ self.assertListEqual(l1, l2)
164
+
165
+
166
+ # TODO: move these to reporting
167
+ # def file_in_format(self, fileextension, path=True):
168
+ # fille = "{}.{}".format(self.test_file_name, fileextension)
169
+ # if path:
170
+ # path = os.path.join(self.test_dir, fille)
171
+ # return path
172
+ # else:
173
+ # return fille
174
+ # def test_table_to_csv(self):
175
+ # pass
176
+ # print('saving '+self.file_in_format('xlsx'))
177
+ # self.test_config.save_csv(self.file_in_format('csv'))
178
+ # self.assertIn(self.file_in_format('csv',False), os.listdir(self.test_dir))
179
+
180
+ #
181
+ # def test_table_to_excel(self):
182
+ # pass
183
+ # #print('saving '+self.file_in_format('xlsx'))
184
+ # #self.test_config.save_excel(self.file_in_format('xlsx'))
185
+ # #self.assertIn(self.file_in_format('xlsx',False),os.listdir(self.test_dir))
186
+ #
187
+ # def test_table_to_gsheets(self):
188
+ # pass
189
+ #
190
+ # def test_table_to_db(self):
191
+ # pass
192
+
193
+
194
+ @forge
195
+ class Static(System):
196
+ attrs_prop: float = attr.ib(1.0)
197
+ attrs_str: str = attr.ib("hey now")
198
+
199
+ @system_property
200
+ def test_one(self) -> int:
201
+ return 1
202
+
203
+ @system_property()
204
+ def test_two(self) -> int:
205
+ return 2
206
+
207
+
208
+ class TestStatic(unittest.TestCase):
209
+ def setUp(self):
210
+ self.test_config = Static()
211
+
212
+ def test_static(self, num=10):
213
+ cur_dict = self.test_config.data_dict
214
+ with ProblemExec(self.test_config, {}) as px:
215
+ for i in range(num):
216
+ with ProblemExec(self.test_config, {}) as px:
217
+ px.save_data()
218
+ postdict = self.test_config.data_dict
219
+ self.assertDictEqual(cur_dict, postdict)
220
+
221
+ self.assertEqual(len(self.test_config.dataframe), 1)
222
+
223
+ def test_input(self, num=10):
224
+ cur_dict = self.test_config.data_dict
225
+ with ProblemExec(self.test_config, {}) as px:
226
+ for i in range(num):
227
+ with ProblemExec(self.test_config, {}) as px:
228
+ self.test_config.attrs_prop = i + self.test_config.attrs_prop
229
+ px.save_data()
230
+ postdict = self.test_config.data_dict
231
+ self.assertDictEqual(cur_dict, postdict)
232
+
233
+ self.assertEqual(len(self.test_config.dataframe), num)
engforge/typing.py ADDED
@@ -0,0 +1,92 @@
1
+ """ """
2
+
3
+ import pandas, attr, numpy
4
+ from engforge.properties import *
5
+ import attrs
6
+
7
+ # import matplotlib.pyplot as plt
8
+ pandas.set_option("use_inf_as_na", True)
9
+
10
+ # Type Checking
11
+ NUMERIC_TYPES = (float, int, numpy.int64, numpy.float64)
12
+ NUMERIC_NAN_TYPES = (float, int, type(None), numpy.int64, numpy.float64)
13
+ STR_TYPES = (str, numpy.string_)
14
+ TABLE_TYPES = (int, float, str, type(None), numpy.int64, numpy.float64)
15
+
16
+
17
+ # TODO: add min / max args & attrs boilerplate
18
+ def NUMERIC_VALIDATOR():
19
+ return attr.validators.instance_of(NUMERIC_TYPES)
20
+
21
+
22
+ def NUMERIC_NAN_VALIDATOR():
23
+ return attr.validators.instance_of(NUMERIC_NAN_TYPES)
24
+
25
+
26
+ def STR_VALIDATOR():
27
+ return attr.validators.instance_of(STR_TYPES)
28
+
29
+
30
+ ATTR_VALIDATOR_TYPES = (
31
+ attr.validators._AndValidator,
32
+ attr.validators._InstanceOfValidator,
33
+ attr.validators._MatchesReValidator,
34
+ attr.validators._ProvidesValidator,
35
+ attr.validators._OptionalValidator,
36
+ attr.validators._InValidator,
37
+ attr.validators._IsCallableValidator,
38
+ attr.validators._DeepIterable,
39
+ attr.validators._DeepMapping,
40
+ )
41
+
42
+ TAB_VALIDATOR_TYPE = (
43
+ attr.validators._InstanceOfValidator
44
+ ) # our validators should require a type i think, at least for tabulation
45
+
46
+
47
+ # Improved Attrs Creation Fields
48
+ def Options(*choices, **kwargs):
49
+ """creates an attrs field with validated choices on init and setattr
50
+ :param choices: a list of choices that are validated on input, the first becoming the default unless it is passed in kwargs
51
+ :param kwargs: keyword args passed to attrs field"""
52
+ assert choices, f"must have some choices!"
53
+ assert "type" not in kwargs, "options type set is str"
54
+ valids = set((str, int, float, bool, type(None)))
55
+ assert set([type(c) for c in choices]).issubset(
56
+ valids
57
+ ), f"choices must be in {valids}"
58
+ assert "on_setattr" not in kwargs
59
+
60
+ validators = [attrs.validators.in_(choices)]
61
+
62
+ # Merge Validators
63
+ if "validators" in kwargs:
64
+ in_validators = kwargs.pop("validators")
65
+ if isinstance(in_validators, list):
66
+ validators.extend(in_validators)
67
+ elif isinstance(in_validators, attr.validators._ValidatorType):
68
+ validators.append(in_validators)
69
+ else:
70
+ raise ValueError(f"bad validator {in_validators}")
71
+
72
+ # Default
73
+ if "default" in kwargs:
74
+ default = kwargs.pop("default")
75
+ assert type(default) is str
76
+ else:
77
+ default = choices[0]
78
+
79
+ on_setattr = [attrs.setters.validate]
80
+
81
+ # Create The Attr!
82
+ a = attrs.field(
83
+ default=default,
84
+ type=str,
85
+ validator=validators,
86
+ on_setattr=on_setattr,
87
+ **kwargs,
88
+ )
89
+ return a
90
+
91
+
92
+ # def Numeric #TODO with min/max that is enforced in solver!