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,338 @@
1
+ """Testing of costs reporting module
2
+ - emphasis on recursive testing w/o double accounting on instance basis (id!)
3
+ - emphasis on default / subclass adjustments
4
+ """
5
+
6
+ import unittest
7
+ from engforge.components import Component
8
+ from engforge._testing_components import *
9
+
10
+
11
+ class TestEconDefaults(unittest.TestCase):
12
+ def tearDown(self) -> None:
13
+ Comp1.reset_cls_costs()
14
+ EconDefault.reset_cls_costs()
15
+
16
+ def test_custom_costs(self):
17
+ ed = EconDefault()
18
+ self.assertNotIn("comp1", ed.internal_components())
19
+
20
+ ed.custom_cost("comp1", Comp1())
21
+ self.assertIn("comp1", ed.internal_components())
22
+
23
+ def test_non_costmodel_default(self):
24
+ EconDefault.default_cost("comp", Comp1(cost_per_item=10))
25
+ ed = EconDefault(comp=Component())
26
+ ed.run()
27
+ self.assertEqual(ed.combine_cost, 10)
28
+ self.assertEqual(ed.econ.combine_cost, 10)
29
+
30
+
31
+ class TestCategoriesAndTerms(unittest.TestCase):
32
+ def tearDown(self) -> None:
33
+ Comp1.reset_cls_costs()
34
+ Comp2.reset_cls_costs()
35
+ EconRecursive.reset_cls_costs()
36
+ TermCosts.reset_cls_costs()
37
+
38
+ def test_itemized_costs(self):
39
+ tc = TermCosts()
40
+
41
+ ct = tc.costs_at_term(0)
42
+ self.assertEqual(ct["cost_init"], 100)
43
+ self.assertEqual(ct["cost_maintenance"], 0)
44
+ self.assertEqual(ct["cost_tax"], 1)
45
+ self.assertEqual(ct["cost_wage_tax"], 0)
46
+
47
+ ct = tc.costs_at_term(1)
48
+ self.assertEqual(ct["cost_init"], 00)
49
+ self.assertEqual(ct["cost_maintenance"], 10)
50
+ self.assertEqual(ct["cost_tax"], 1)
51
+ self.assertEqual(ct["cost_wage_tax"], 0)
52
+
53
+ ct = tc.costs_at_term(2)
54
+ self.assertEqual(ct["cost_init"], 00)
55
+ self.assertEqual(ct["cost_maintenance"], 10)
56
+ self.assertEqual(ct["cost_tax"], 1)
57
+ self.assertEqual(ct["cost_wage_tax"], 15)
58
+
59
+ def test_category_costs(self):
60
+ tc = TermCosts()
61
+ cc = tc.cost_categories_at_term(0)
62
+ self.assertEqual(cc["capex"], 100)
63
+ self.assertEqual(cc["opex"], 0)
64
+ self.assertEqual(cc["tax"], 1)
65
+
66
+ tc = TermCosts()
67
+ cc = tc.cost_categories_at_term(2)
68
+ self.assertEqual(cc["capex"], 0)
69
+ self.assertEqual(cc["opex"], 25)
70
+ self.assertEqual(cc["tax"], 16)
71
+
72
+ def test_econ(self):
73
+ tc = TermCosts()
74
+ Comp2.default_cost("comp1", 50)
75
+ c2 = Comp2(cost_per_item=10)
76
+ er = EconRecursive(comp1=tc, comp2=c2)
77
+ # inkw = {'econ_term_length':[1,5,10,50],'econ_discount_rate':[0,0.01,0.05,0.1]}
78
+ inkw = {}
79
+ er.run(revert_last=False, **inkw)
80
+
81
+ d = er.data_dict
82
+
83
+ self.assertEqual(d["econ.lifecycle.term_cost"], 161)
84
+ self.assertEqual(d["econ.summary.total_cost"], 161)
85
+ self.assertEqual(d["combine_cost"], 161)
86
+ self.assertEqual(d["sub_items_cost"], 161)
87
+
88
+ def test_econ_array(self):
89
+ tc = TermCosts()
90
+ Comp2.default_cost("comp1", 50)
91
+ c2 = Comp2(cost_per_item=10)
92
+ er = EconRecursive(comp1=tc, comp2=c2)
93
+ inkw = {"econ.term_length": [0, 5, 10, 15], "econ.fixed_output": 1}
94
+ # inkw = {}
95
+ er.run(revert_last=False, revert_every=False, **inkw)
96
+
97
+ df = er.dataframe
98
+ tc = (
99
+ df["econ.summary.total_cost"] == np.array([161.0, 220.0, 305.0, 390.0])
100
+ ).all()
101
+ self.assertTrue(tc)
102
+
103
+
104
+ class TestEconomicsAccounting(unittest.TestCase):
105
+ def setUp(self) -> None:
106
+ pass
107
+
108
+ def tearDown(self) -> None:
109
+ Comp1.reset_cls_costs()
110
+ Comp2.reset_cls_costs()
111
+ EconRecursive.reset_cls_costs()
112
+
113
+ def test_econ_defaults(self):
114
+ Comp1.default_cost("norm", Norm(cost_per_item=10))
115
+ Comp2.default_cost("comp1", Comp1(cost_per_item=5))
116
+ EconRecursive.default_cost("comp2", Comp2(cost_per_item=3))
117
+ er = EconRecursive(cost_per_item=50)
118
+ # c2 = er.comp2
119
+ self.assertEqual(er.combine_cost, 78)
120
+ self.assertEqual(er.sub_items_cost, 28)
121
+
122
+ er.run()
123
+ d = er.data_dict
124
+ self.assertEqual(78, d["econ.summary.total_cost"])
125
+ self.assertEqual(78, d["econ.lifecycle.annualized.term_cost"])
126
+ self.assertEqual(78, d["econ.lifecycle.annualized.levelized_cost"])
127
+ self.assertEqual(78, d["econ.lifecycle.term_cost"])
128
+
129
+ def test_recursive_null(self, ANS=75):
130
+ Comp1.default_cost("norm", 5)
131
+ Comp2.default_cost("comp1", 10)
132
+ EconRecursive.default_cost("comp1", 3)
133
+ EconRecursive.default_cost("comp2", 7)
134
+ er = EconRecursive(cost_per_item=50, comp1=None, comp2=None)
135
+ er.run()
136
+ self.assertEqual(er.combine_cost, ANS)
137
+ self.assertEqual(er.econ.combine_cost, ANS)
138
+ self.assertEqual(er.comp1, None)
139
+ self.assertEqual(er.comp2, None)
140
+
141
+ d = er.data_dict
142
+ self.assertEqual(ANS, d["econ.combine_cost"])
143
+ self.assertEqual(ANS, d["combine_cost"])
144
+ self.assertEqual(3, d["econ.comp1.cost.item_cost"])
145
+ self.assertEqual(7, d["econ.comp2.cost.item_cost"])
146
+
147
+ def test_recursive_comp2(self, ANS=80):
148
+ Comp1.default_cost("norm", 5)
149
+ Comp2.default_cost("comp1", 10)
150
+ EconRecursive.default_cost("comp1", Comp1(cost_per_item=3))
151
+ EconRecursive.default_cost("comp2", Comp2(cost_per_item=7))
152
+ er = EconRecursive(cost_per_item=50, comp1=None, comp2=None)
153
+ er.run()
154
+ self.assertEqual(er.combine_cost, ANS)
155
+ self.assertEqual(er.econ.combine_cost, ANS)
156
+
157
+ d = er.data_dict
158
+ self.assertEqual(ANS, d["econ.combine_cost"])
159
+ self.assertEqual(ANS, d["combine_cost"])
160
+ self.assertEqual(3, d["econ.comp1.cost.item_cost"])
161
+ # self.assertEqual(10,d['econ_comp2.combine_cost'])
162
+
163
+ def test_recursive_all(self, ANS=80):
164
+ # FIXME: missing norm cost of -5
165
+
166
+ Comp1.default_cost("norm", 5)
167
+ Comp2.default_cost("comp1", 10)
168
+ EconRecursive.default_cost("comp1", Comp1(cost_per_item=3))
169
+ EconRecursive.default_cost("comp2", Comp2(cost_per_item=7))
170
+ er = EconRecursive(cost_per_item=50)
171
+ er.run()
172
+ self.assertEqual(er.combine_cost, ANS)
173
+ self.assertEqual(er.econ.combine_cost, ANS)
174
+
175
+ d = er.data_dict
176
+ self.assertEqual(ANS, d["econ.combine_cost"])
177
+ self.assertEqual(ANS, d["combine_cost"])
178
+ # self.assertEqual(5,d['econ_comp1.combine_cost'])
179
+ # self.assertEqual(10,d['econ_comp2.combine_cost'])
180
+ self.assertEqual(10, d["econ.comp2.comp1.cost.item_cost"])
181
+ self.assertEqual(5, d["econ.comp1.norm.cost.item_cost"])
182
+
183
+
184
+ class TestCostModel(unittest.TestCase):
185
+ def setUp(self):
186
+ pass
187
+
188
+ def tearDown(self) -> None:
189
+ Comp1.reset_cls_costs()
190
+ Comp2.reset_cls_costs()
191
+
192
+ def test_comp1(self):
193
+ c0 = Comp1(cost_per_item=0)
194
+ self.assertEqual(c0.combine_cost, 0)
195
+
196
+ c1 = Comp1(cost_per_item=5)
197
+ self.assertEqual(c1.combine_cost, 5)
198
+
199
+ c2 = Comp1(cost_per_item=10)
200
+ self.assertEqual(c2.combine_cost, 10)
201
+
202
+ def test_comp2(self):
203
+ c1 = Comp1(cost_per_item=0)
204
+ c2 = Comp2(cost_per_item=0, comp1=c1)
205
+ self.assertEqual(c2.combine_cost, 0)
206
+
207
+ c1 = Comp1(cost_per_item=5)
208
+ c2 = Comp2(cost_per_item=5, comp1=c1)
209
+ self.assertEqual(c2.combine_cost, 10)
210
+
211
+ c1 = Comp1(cost_per_item=10)
212
+ c2 = Comp2(cost_per_item=10, comp1=c1)
213
+ self.assertEqual(c2.combine_cost, 20)
214
+
215
+ def test_default(self):
216
+ c1 = Comp1(cost_per_item=0)
217
+ self.assertEqual(c1.combine_cost, 0)
218
+
219
+ Comp1.default_cost("norm", 10)
220
+ c1 = Comp1(cost_per_item=0)
221
+ self.assertEqual(c1.combine_cost, 10)
222
+
223
+ c1 = Comp1(cost_per_item=0)
224
+ c1.custom_cost("norm", 20)
225
+ self.assertEqual(c1.combine_cost, 20)
226
+ self.assertEqual(Comp1._slot_costs["norm"], 10)
227
+
228
+ def test_ref_loop(self):
229
+ # Comp1.default_cost('norm',10)
230
+ c2 = Comp2(cost_per_item=5)
231
+ c1 = Comp1(cost_per_item=5, norm=c2)
232
+ c2.comp1 = c1
233
+ self.assertEqual(c1.combine_cost, 10)
234
+ self.assertEqual(c1.sub_items_cost, 10)
235
+
236
+ def test_override(self):
237
+ Comp1.default_cost("norm", 10)
238
+ # c2 = Comp2(cost_per_item=5)
239
+ c1 = Comp1(cost_per_item=5) # ,norm=c2)
240
+ self.assertEqual(c1.combine_cost, 15)
241
+ self.assertEqual(c1.sub_items_cost, 10)
242
+
243
+ # Comp1.reset_cls_costs()
244
+ c2 = Comp2(cost_per_item=15)
245
+ c1 = Comp1(cost_per_item=15, norm=c2)
246
+ self.assertEqual(c1.combine_cost, 40)
247
+ self.assertEqual(c1.sub_items_cost, 25)
248
+
249
+ def test_defaults(self):
250
+ # Comp1.reset_cls_costs()
251
+ Comp1.default_cost("norm", Norm(cost_per_item=10))
252
+ Comp2.default_cost("comp1", Comp1(cost_per_item=5))
253
+ c2 = Comp2(cost_per_item=3)
254
+ self.assertEqual(c2.combine_cost, 18)
255
+ self.assertEqual(c2.sub_items_cost, 15)
256
+
257
+
258
+ class TestFanSystemDataFrame(unittest.TestCase):
259
+ def test_dataframe(self):
260
+ # Create the FanSystem instance
261
+ fs = FanSystem(fan=Fan(), motor=Motor(), base=MetalBase())
262
+ fs.run(
263
+ **{
264
+ "econ.term_length": [1, 10, 20, 50],
265
+ "fan.V": [1, 5, 10],
266
+ "fan.area": [1, 5, 10],
267
+ },
268
+ base=[None, MetalBase()],
269
+ )
270
+
271
+ # Get the dataframe
272
+ df_complete = fs.last_context.dataframe
273
+
274
+ dfc = df_complete
275
+ match = (
276
+ dfc["fan.blade_cost"] + dfc["motor.motor_cost"]
277
+ == dfc["econ.lifecycle.category.capex"]
278
+ ).all()
279
+ self.assertTrue(match)
280
+
281
+ match = (
282
+ df_complete["fan.area"] * df_complete["fan.v"]
283
+ == df_complete["fan.volumetric_flow"]
284
+ ).all()
285
+ self.assertTrue(match)
286
+
287
+
288
+ # TODO: get cost accounting working for ComponentIter components
289
+ # @forge
290
+ # class CompGroup(ComponentIterator):
291
+ # pass
292
+ # # @forge
293
+ # # class CostGroup(CompGroup,CostModel):
294
+ # # pass
295
+
296
+ # @forge
297
+ # class EconWide(System):
298
+ # econ = Slot.define(Economics)
299
+ # comp_set = Slot.define_iterator(CompGroup,wide=True)
300
+ #
301
+ # @forge
302
+ # class EconNarrow(System):
303
+ # econ = Slot.define(Economics)
304
+ # comp_set = Slot.define_iterator(CompGroup,wide=False)
305
+
306
+ # class TestEconomicsIter(unittest.TestCase):
307
+ #
308
+ # def tearDown(self):
309
+ # Comp1.reset_cls_costs()
310
+ # Comp2.reset_cls_costs()
311
+ # EconWide.reset_cls_costs()
312
+ # EconNarrow.reset_cls_costs()
313
+ # CostGroup.reset_cls_costs()
314
+ #
315
+ # def test_comp_wide(self):
316
+ #
317
+ # def test_comp_narrow(self):
318
+ #
319
+ # def test_cost_wide(self):
320
+ #
321
+ # def test_comp_narrow(self):
322
+ # cg = CompGroup(component_type=Comp1)
323
+ # total = 0
324
+ # for i in range(11):
325
+ # cg.data.append(Comp1(cost_per_item=i))
326
+ # total += i
327
+ #
328
+ # ew = EconNarrow(comp_set = cg)
329
+ # ew.run()
330
+ #
331
+ # d = ew.data_dict
332
+ # self.assertEqual(d['econ_comp_set.item_cost'], ew.comp_set.current.item_cost)
333
+ # self.assertEqual(ew.econ.total_cost,total)
334
+ # self.assertEqual(ew.econ.total_cost,total)
335
+
336
+
337
+ if __name__ == "__main__":
338
+ unittest.main()
@@ -0,0 +1,109 @@
1
+ """test transient cases and match against real world results"""
2
+
3
+ import unittest
4
+
5
+ from engforge import *
6
+ from engforge.attr_plotting import *
7
+ from engforge._testing_components import SpringMass
8
+
9
+ from scipy.optimize import curve_fit, least_squares
10
+ import numpy as np
11
+ from matplotlib.pyplot import *
12
+
13
+ import attrs
14
+
15
+ fit = lambda t, a, b, w: a * np.sin((2 * np.pi / w) * t) + b
16
+ jac = lambda t, a, b, w: np.array(
17
+ [
18
+ np.sin(w * t),
19
+ 1 * np.ones(t.shape),
20
+ a * w * np.cos(w * t),
21
+ 0 * np.ones(t.shape),
22
+ ]
23
+ ).T
24
+ ls = lambda x, t, y: (x[0] * np.sin(x[1] * t + x[3]) + x[2]) - y
25
+ f = lambda x, t, y: (x[0] * np.sin(x[1] * t + x[3]) + x[2])
26
+
27
+
28
+ class TestDynamics(unittest.TestCase):
29
+ def test_sim(self):
30
+ """tests free undamped motion"""
31
+
32
+ sm = SpringMass(Fa=0, u=0)
33
+
34
+ # the analytical answer
35
+ self.w_ans = np.sqrt(sm.k / sm.m)
36
+
37
+ dt = 0.001
38
+
39
+ df = sm.simulate(dt=dt, endtime=10)
40
+ # TODO: add passing flag to context
41
+ # self.assertTrue(sm.solved)
42
+ # self.assertTrue(sm.converged)
43
+
44
+ X = df.x
45
+ T = df.time
46
+ t = T[T < 2]
47
+ x = X[T < 2]
48
+
49
+ p0 = (x.max() - x.min(), self.w_ans + 0.01, x.mean(), -3.14)
50
+ # ans,cov = curve_fit(fit,t,x,p0=p0)
51
+ ans = least_squares(
52
+ ls,
53
+ p0,
54
+ args=(t, x),
55
+ ftol=1e-14,
56
+ jac="cs",
57
+ xtol=1e-14,
58
+ gtol=1e-14,
59
+ x_scale=2,
60
+ )
61
+ if not ans.success:
62
+ raise Exception("failure")
63
+
64
+ self.assertAlmostEqual(ans.x[1], self.w_ans, delta=0.01)
65
+
66
+ def test_damping(self):
67
+ """test that the ss answer is equal to the result with damping"""
68
+ sm = SpringMass(Fa=0, u=5)
69
+ sm.run(dxdt=0, combos="time") # time captures integral via combos
70
+
71
+ dfss = sm.last_context.dataframe
72
+ trsm, df = sm.simulate(dt=0.001, endtime=10, return_all=True)
73
+
74
+ self.assertAlmostEqual(df.iloc[-1].x, dfss.iloc[0].x, delta=0.01)
75
+
76
+ def test_forcing(self):
77
+ """test that the ss answer is equal to the result with damping"""
78
+ sm = SpringMass(Fa=10, u=0, wo_f=1)
79
+ sm.wo_f = w_ans = np.sqrt(sm.k / sm.m) * 0.1 # very different frequency
80
+
81
+ sm.run(dxdt=0, combos="time")
82
+ dfss = sm.dataframe
83
+ sm.x = dfss.iloc[0].x # no residual input
84
+
85
+ trsm, df = sm.simulate(
86
+ dt=0.001, endtime=10, run_solver=False, combos="*", return_all=True
87
+ )
88
+
89
+ X = df.x
90
+ T = df.time
91
+ t = T[T < 2]
92
+ x = X[T < 2]
93
+
94
+ p0 = (x.max() - x.min(), w_ans + 0.01, x.mean(), -3.14)
95
+ # ans,cov = curve_fit(fit,t,x,p0=p0)
96
+ ans = least_squares(
97
+ ls,
98
+ p0,
99
+ args=(t, x),
100
+ ftol=1e-14,
101
+ jac="cs",
102
+ xtol=1e-14,
103
+ gtol=1e-14,
104
+ x_scale=2,
105
+ )
106
+ if not ans.success:
107
+ raise Exception("failure")
108
+
109
+ self.assertAlmostEqual(ans.x[1], w_ans, delta=0.01)
@@ -0,0 +1,39 @@
1
+ from engforge._testing_components import *
2
+
3
+ import numpy as np
4
+ import unittest
5
+
6
+
7
+ class TestDynamics(unittest.TestCase):
8
+ def test_dynamics(self, endtime=10, dt=0.01):
9
+ dc = DynamicComponent()
10
+ ds = DynamicSystem(comp=dc)
11
+
12
+ min_kw = {"normalize": np.array([1 / 1000])}
13
+ sim, df = ds.simulate(dt, endtime, run_solver=False, return_all=True)
14
+
15
+ # ax = df.plot("time", ["x", "comp_x", "trns_x", "comp_x0"])
16
+ # # ax.set_ylim(-1,5)
17
+ # ax2 = ax.twinx()
18
+ # ax2.plot(df.time, df.a, "r--", label="acl")
19
+ # ax2.plot(df.time, df["trns_a"], "b--", label="trans_acl")
20
+ # ax2.plot(df.time, df["spring_accel"], "c--", label="spring_acl")
21
+ # ax2.legend()
22
+
23
+ self.assertGreaterEqual(df["time"].max(), endtime - dt)
24
+
25
+ def test_steady_state(self):
26
+ dc = DynamicComponent()
27
+ ds = DynamicSystem(comp=dc)
28
+
29
+ # TODO: check dxdt=0 combo results (dynamics/rates==states)
30
+
31
+ ans = ds.run(dxdt=0, combos="time", revert_last=False, revert_every=False)
32
+ output = ans["output"][0]
33
+ self.assertTrue(output["success"])
34
+
35
+ all_rate_res = output["Ycon"]
36
+ for rkey, rate_res in all_rate_res.items():
37
+ self.assertAlmostEqual(
38
+ rate_res, 0, places=3, msg=f"Rate {rkey} is not zero"
39
+ )
@@ -0,0 +1,97 @@
1
+ from engforge import *
2
+ import attrs
3
+ import numpy
4
+
5
+
6
+ @forge(auto_attribs=True)
7
+ class FourBar(System):
8
+ # r1
9
+ r1: float = 0.02
10
+ gamma_zero: float = 10 * 3.1415 / 180 # radians
11
+ gamma: float = 0
12
+ r1_slv = Solver.declare_var("r1", combos="indep,r1")
13
+ r1_slv.add_var_constraint(0.0, kind="min", combos="lim")
14
+ r1_slv.add_var_constraint(0.05, kind="max", combos="lim")
15
+
16
+ gz_slv = Solver.declare_var("gamma_zero", combos="indep,zero")
17
+ gz_slv.add_var_constraint(0.0, kind="min", combos="lim")
18
+ gz_slv.add_var_constraint(15 * 3.1415 / 180, kind="max", combos="lim")
19
+
20
+ gam_slv = Solver.declare_var("gamma", combos="indep,gamma")
21
+ gam_slv.add_var_constraint(0.0, kind="min", combos="lim")
22
+ gam_sig = Signal.define("gamma", "gamma_zero", combos="gam_sig")
23
+
24
+ # arm
25
+ theta: float = 0 # radians
26
+ ra: float = 0.03
27
+ theta_slv = Solver.declare_var("theta", combos="indep,theta")
28
+ theta_slv.add_var_constraint(0.0, kind="min", combos="lim")
29
+ theta_slv.add_var_constraint(3.14159, kind="max", combos="lim")
30
+
31
+ # r2
32
+ x2: float = 0.05
33
+ h2: float = 0.0
34
+ x2_slv = Solver.declare_var("x2", combos="indep,x2")
35
+ x2_slv.add_var_constraint(0.025, kind="min", combos="lim")
36
+ x2_slv.add_var_constraint(0.05, kind="max", combos="lim")
37
+
38
+ h2_slv = Solver.declare_var("h2", combos="indep,h2")
39
+ h2_slv.add_var_constraint(-0.01, kind="min", combos="lim")
40
+ h2_slv.add_var_constraint(0.01, kind="max", combos="lim")
41
+
42
+ len_slv = Solver.con_eq("l3_zero", "l3_gamma", combos="goal,leneq")
43
+ goal_slv = Solver.objective("gamma_goal", kind="max", combos="goal,gamma")
44
+ l3_max = Solver.objective("l3_zero", kind="max", combos="goal,l3max")
45
+
46
+ @system_prop
47
+ def gamma_goal(self) -> float:
48
+ return self.gamma
49
+
50
+ @system_prop
51
+ def r3_x_zero(self) -> float:
52
+ """length defined by closed condition"""
53
+ return self.x2 + self.ra - self.r1 * numpy.cos(self.gamma_zero)
54
+
55
+ @system_prop
56
+ def r3_x(self) -> float:
57
+ """length defined by gamma/theta"""
58
+ return (
59
+ self.x2 + self.ra * numpy.cos(self.theta) - self.r1 * numpy.cos(self.gamma)
60
+ )
61
+
62
+ @system_prop
63
+ def y_zero(self) -> float:
64
+ """length defined by closed condition"""
65
+ return self.h2 - self.r1 * numpy.sin(self.gamma_zero)
66
+
67
+ @system_prop
68
+ def y(self) -> float:
69
+ """length defined by gamma/theta"""
70
+ return (
71
+ self.x2 + self.ra * numpy.sin(self.theta) - self.r1 * numpy.sin(self.gamma)
72
+ )
73
+
74
+ @system_prop
75
+ def l3_zero(self) -> float:
76
+ return self.r3_x_zero**2 + self.y_zero**2
77
+
78
+ @system_prop
79
+ def l3_gamma(self) -> float:
80
+ return self.r3_x**2 + self.y**2
81
+
82
+
83
+ if __name__ == "__main__":
84
+ # %run -i ~/engforge/engforge/test/test_four_bar.py
85
+ import numpy as np
86
+
87
+ fb = FourBar()
88
+ fb.run(combos="*", revert_last=False, revert_every=False)
89
+
90
+ df = fb.last_context.dataframe
91
+
92
+ fb.run(
93
+ combos="goal,gamma,theta,lim",
94
+ theta=np.linspace(0, 3.14159),
95
+ revert_last=False,
96
+ revert_every=False,
97
+ )
@@ -0,0 +1,72 @@
1
+ import pathlib
2
+
3
+ testpath = pathlib.Path(__file__).parent.absolute()
4
+
5
+ import logging
6
+
7
+ log = logging.getLogger("neptunya-test")
8
+
9
+ import sys
10
+
11
+
12
+ from matplotlib import pylab
13
+ import numpy as n
14
+ import unittest
15
+
16
+ import engforge
17
+
18
+
19
+ import copy, traceback
20
+ import random, json
21
+ import time
22
+
23
+
24
+ class ImportTest(unittest.TestCase):
25
+ """We test the compilation of all included modules"""
26
+
27
+ def test_import_analysis(self):
28
+ import engforge.analysis
29
+
30
+ def test_import_common(self):
31
+ import engforge.common
32
+
33
+ def test_import_components(self):
34
+ import engforge.components
35
+
36
+ def test_import_configuration(self):
37
+ import engforge.configuration
38
+
39
+ def test_import_data(self):
40
+ import engforge.datastores.data
41
+
42
+ # def test_import_gdocs(self):
43
+ # import engforge.datastores.gdocs
44
+
45
+ def test_import_locations(self):
46
+ import engforge.locations
47
+
48
+ def test_import_logging(self):
49
+ import engforge.logging
50
+
51
+ def test_import_plotting(self):
52
+ import engforge.attr_plotting
53
+
54
+ # def test_import_process(self):
55
+ # import engforge.process
56
+
57
+ def test_import_patterns(self):
58
+ import engforge.patterns
59
+
60
+ def test_import_solid_materials(self):
61
+ import engforge.eng.solid_materials
62
+
63
+ def test_import_tabulation(self):
64
+ import engforge.tabulation
65
+
66
+ def test_import_thermodynamics(self):
67
+ import engforge.eng.thermodynamics
68
+
69
+
70
+ if __name__ == "__main__":
71
+ print(("\n" * 10))
72
+ unittest.main()