1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """The MBDyn service available from Python by L{WraptMBDyn}. This module
23 handles also the recovery of all the pointers to the C++ instances
24 by creating Python objects, described as a start in
25 L{mbdyn.bindings.basic_objects}.
26
27 An interface to all the MBDyn groups is also provided by L{Groups}.
28 """
29 import os
30 import time
31
32 import mbdyn.bindings.swigModule as swigModule
33 import mbdyn.bindings.progressbar as progressbar
34
35 from mbdyn.bindings.basic_objects import BASIC_CLASS
36 from mbdyn.bindings.groups import GROUP
37 from mbdyn.bindings.frames import ReferenceFrame, FramesList
38
39
41 """The MBDyn progress bar. Used for displaying the
42 C{Solver} advance and based on the progress bar defined
43 in L{mbdyn.bindings.progressbar}.
44 """
45
53
55 """Create a progress bar according to the final time"""
56 self.pbar = progressbar.ProgressBar(maxval=final_time,
57 widgets=self.widgets)
58 self.pbar.start()
59
60 - def update(self, current_time):
61 """Update the progress bar"""
62 self.pbar.update(current_time)
63
65 """Pause the progress bar at the time given. The bar will
66 be seen in the console."""
67 self.pbar.stand_by(current_time)
68
70 """Will write a new bar in the console"""
71 self.pbar.restart(current_time)
72
74 """End of the progress bar"""
75 self.pbar.finish()
76
77
79 """Gather MBDyn groups of nodes or elements.
80 The nodes and elements
81 are organised into groups by the C{DataManager}, described in the file
82 'I{dataman.h}'. The SWIG interface to the C{DataManager} is able to get
83 back the objects pointers, so then they can create Python objects as
84 described in L{mbdyn.bindings.basic_objects}. The SWIG interface
85 of the C{DataManager} is described in 'I{dataman.i}' and the instance
86 of that class is C{pDM}, attribute of the MBDyn C{Solver}.
87
88 This class will interact with C{pDM} and get back all
89 the MBDyn objects. However this step requires to have a corresponding
90 group where to store the object reference. That is the role of
91 the L{mbdyn.bindings.groups} module.
92
93 Moreover MBDyn uses a polymorphism on the C{Elem} class or C{Node} class.
94 This single class definition will
95 try to be converted to a general class for the group, this is achieved
96 in the method L{try_to_convert}.
97
98 Finally this class is responsible of listing present groups. For example::
99
100 >>> wm = WraptMBDyn("input_file")
101 >>> wm.init()
102 >>> wm.elts
103 [automatic_structurals,
104 bodies,
105 joints,
106 beams,
107 forces]
108
109 C{elts} is a C{Groups} instance.
110 """
111
113 """Translation of the C++ enumeration done in MBDyn, each key
114 corresponds to an integer, indice of the group."""
115 self.identities = []
116 self.list = []
117 self.list_keys = []
118 for group_id, group_key in enumerate(GROUP[item_key]["KEYS"]):
119 self.identities.append((group_key, group_id))
120
122 """Add a group of elements or nodes"""
123 self.list.append(group)
124 self.list_keys.append(group.key)
125
127 """Return a group from the MBDyn key (STRUCTURAL, FORCE...)"""
128 return self.list[self.list_keys.index(key)]
129
131 """Try to call the C{convert} method for each group.
132 Due to the numerous groups, the difficulty is to make
133 a package that work without having an interface for everyone.
134 That's why this method tests if the SWIG module has
135 a function to convert the object from the
136 L{mbdyn.bindings.basic_objects} to a general class of the group.
137 If the SWIG module has a conversion to offer, the C{convert} method
138 of the group will be called by supplying the function reference.
139 The conversion details can then be found in L{mbdyn.bindings.groups}.
140 """
141 for group in self.list:
142 convert_to_class_name = group.convert_method_name
143 try:
144 convert_to_class = getattr(swigModule, convert_to_class_name)
145 group.convert(convert_to_class)
146 except AttributeError:
147 pass
148
150 """The present groups are set as attribute names.
151 The names used are defined in the
152 C{GROUP_TABLE} of L{mbdyn.bindings.groups}."""
153 for group in self.list:
154 setattr(self, group.attr_name, group)
155
157 """Return all the present groups"""
158 string = "["
159 string += ",\n".join(group.attr_name for group in self.list)
160 string += "]"
161 return string
162
163
165 """The MBDyn service for Python. C{WraptMBDyn} can run only
166 if it receives a MBDyn input file. In case the input file
167 is not in the current directory, the path has to be given by
168 C{working_dir}. Like in MBDyn, the output file can be omitted.
169 By defaults the results file are not written, so the simulation
170 is going faster and this is the user duty to choose what he wants
171 to save. The L{mbdyn} module offers an interface for those
172 functionalities.
173 This behavior can be turned off by L{write_mbdyn_files}.
174
175 C{WraptMBDyn} performs 4 calls that are empty, because they do not deal
176 with its work, but they are use for an interface. They are:
177
178 - L{do_as_init}
179 - L{update}
180 - L{save}
181 - L{final_action}
182
183 The first user of those calls is L{Simulation<mbdyn.main.Simulation>} but
184 the user could develop what he wants.
185
186 The MBDyn object tree is available once the L{init} method has been called.
187 Then the integration can be completely performed by L{run_full} or until
188 a particular time L{run_until}.
189 An demonstration of use for the file 'I{cantilever1}' from the
190 U{MBDyn tutorial
191 <http://www.aero.polimi.it/~mbdyn/documentation/tutorials/node10.html>}
192 saved in the directory 'I{input_file}'::
193
194 >>> from mbdyn import *
195 >>> wm = WraptMBDyn("cantilever1", "input_file")
196 >>> wm.init()
197 ..
198 >>> wm
199 [elts,
200 nodes]
201 >>> wm.write_mbdyn_files(True)
202 >>> wm.run_until(3.)
203 MBDyn progress: 49% [########################### ]
204 >>> wm.solver.current_time
205 2.999000072479248
206 >>> wm.run_full()
207 MBDyn progress: 100% [########################################################]
208 End of simulation at time 6.0 after 6000 steps
209 total iterations: 6075
210 total Jacobians: 6075
211 total error: 0.002095883945
212 CPU time consumed: 6.98 seconds
213
214 Nevertheless L{run_until} is interesting only for an operation not run
215 at each time step, else a reference to L{update} should be supplied.
216 """
217
218 - def __init__(self, input_file=None, working_dir=".",
219 output_file=None, write_output_files=False):
220 self.input_file = input_file
221 self.working_dir = working_dir
222 self.output_file = output_file
223 self.write_output_files = write_output_files
224
225 self.swig_mbdyn = None
226 self.groups = {}
227 self.elts = None
228 self.nodes = None
229 self.frames = None
230 self.representation = "[]"
231
232 self.has_run_init = False
233 self.solver = None
234 self.current_dir = os.getcwd()
235
236 self._has_run_first_step = False
237 self.solver_status = "go"
238 self.pbar = None
239 self.cpu_tstart = None
240 self.continue_iter = True
241
242 if input_file != None:
243 self.create_swig_mbdyn(input_file, working_dir, output_file)
244
246 """Return the output file name for MBDyn"""
247 return self.input_file + "_result"
248
250 """Create MBDyn in its SWIG version"""
251 self.input_file = input_file
252 self.working_dir = working_dir
253 if output_file == None:
254 self.output_file = self._get_output_file()
255 else:
256 self.output_file = output_file
257 self.swig_mbdyn = swigModule.WraptMBDyn(self.input_file,
258 self.output_file)
259
261 """Tell if the MBDyn result files, in ASCII, need to be written
262 or not, by default there are not. The simulation will run faster
263 but the saving process will have to be handled by the user
264 script or L{mbdyn}."""
265 self.write_output_files = boolean
266
268 """Start the communication with MBDyn objects.
269 First the MBDyn solver is created, which corresponds
270 to the C{main} function of 'I{mbdyn.cc}'. The important
271 C{solver} attributes for the interface are:
272
273 - current_time
274 - time_step
275 - final_time
276
277 C{current_time} and C{time_step} are updated at each step.
278 The solver will run the input file parsing and thus fill
279 the C{DataManager}. The tree for navigating the MBDyn
280 objects is then created in L{_get_items}. Finally,
281 the customizable L{do_as_init} method is called
282 during that step.
283 """
284 os.chdir(self.working_dir)
285 if not self.has_run_init:
286 self.swig_mbdyn.create_solver()
287 self.solver = self.swig_mbdyn.solver
288 self.solver.do_output = self.write_output_files
289 self.solver.run_init()
290 self._get_items()
291 self.do_as_init()
292 self.has_run_init = True
293 else:
294 print "'init()' has already been called, no effect"
295 os.chdir(self.current_dir)
296
298 """Scan the reference frames, nodes and elements from
299 the C{DataManager} and create a tree of objects.
300 The process is organised as follow:
301
302 1. Get the all groups for nodes and elements,
303 this step is done by L{_get_item_groups}.
304
305 2. Fill those groups with a general class for nodes
306 and elements, defined in L{mbdyn.bindings.basic_objects}.
307 L{_fill_item_groups} do that part.
308
309 3. Every group try to convert the top reference into a more
310 a more specific one. This part is contained in
311 L{try_to_convert<Groups.try_to_convert>} of L{Groups}.
312
313 4. Nodes and elements can now be accessed on the instance by
314 L{_set_group_as_attributes}
315
316 5. The simulation may have reference frames, it will be tested
317 in L{_try_get_frames}
318 """
319 self._get_item_groups()
320 self._fill_item_groups()
321 for item_key in ["NODE", "ELEM"]:
322 self.groups[item_key].try_to_convert()
323 self._set_group_as_attributes()
324 self._try_get_frames()
325
327 """Create all the groups existing in MBDyn.
328 For each possible group of nodes or elements, ask to the
329 C{DataManager} the number of items. If there is at least one,
330 the corresponding group defined in L{mbdyn.bindings.groups}
331 is created.
332 """
333 for item_key in ["NODE", "ELEM"]:
334 self.groups[item_key] = Groups(item_key)
335
336 get_number = {
337 "NODE" : self.solver.pDM.get_number_nodes,
338 "ELEM" : self.solver.pDM.get_number_elements
339 }
340 for item_key in ["NODE", "ELEM"]:
341 for group_key, group_id in self.groups[item_key].identities:
342 nb_item = get_number[item_key](group_id)
343 if nb_item != 0:
344 ItemGroupClass = GROUP[item_key]["CLASS"][group_id]
345 self.groups[item_key].add_group(ItemGroupClass(group_key,
346 group_id,
347 nb_item))
348
350 """Fill the created groups of MBDyn object references.
351 The two classes used are C{Node} and C{Element} from
352 L{mbdyn.bindings.basic_objects}"""
353 get_item = {
354 "NODE" : self.solver.pDM.get_node,
355 "ELEM" : self.solver.pDM.get_element
356 }
357 for item_key in ["NODE", "ELEM"]:
358 for group in self.groups[item_key].list:
359 for item_id in range(group.number):
360 pitem = get_item[item_key](group.idx, item_id)
361 item = BASIC_CLASS[item_key](pitem)
362 group.add_item(item)
363
365 """Create the interface tree for nodes and elements.
366 Each created group of nodes or elements gets its attribute
367 name set on its corresponding L{Groups}, C{nodes} or C{elts}.
368 Those last ones are as well set on the C{WraptMBDyn}."""
369 for item_group in self.groups.values():
370 item_group.set_group_as_attributes()
371 self.nodes = self.groups["NODE"]
372 self.elts = self.groups["ELEM"]
373 self.representation = "[elts,\nnodes]"
374
376 """Create the reference frames tree in case the input file
377 has some. This method manipulates the C{MBDynParser}
378 for getting the L{ReferenceFrame<mbdyn.bindings.frames>}
379 references and fill the L{FramesList<mbdyn.bindings.frames>}
380 """
381 mbdyn_parser = self.swig_mbdyn.HP
382 nb_frames = mbdyn_parser.get_reference_frame_nb()
383 if nb_frames != 0:
384 self.frames = FramesList()
385 for indice in range(nb_frames):
386 pframe = mbdyn_parser.get_reference_frame(indice)
387 self.frames.add(ReferenceFrame(pframe))
388 self.representation = "[elts,\nnodes,\nframes]"
389
391 """Return the representation of the object tree"""
392 return self.representation
393
395 """Not used on C{WraptMBDyn} called during the L{init} method."""
396 pass
397
399 """Called before starting a new MBDyn time step. This method
400 is empty for C{WraptMBDyn} but is of use in case the user wants
401 to do change the MBDyn objects at run time."""
402 pass
403
405 """This is not the job of C{WraptMBDyn} but called before to start
406 the integration and at the end of each time step"""
407 pass
408
409
411 """The final call at the end of the integration. For C{WraptMBDyn},
412 nothing is done in that step but it may be of use for the class
413 manipulating the service."""
414 pass
415
417 """Check if the first step has been run, else
418 restart the progress bar"""
419 if not self._has_run_first_step:
420 self._run_first_step()
421 else:
422 self.pbar.restart(self.solver.current_time)
423
429
431 """Run until the stop time. Note that this method
432 has to be used with care. If the stop time is over the final time,
433 the integration will anyway continue."""
434 self._check_status()
435
436
437
438 t_future = self.solver.current_time + \
439 self.solver.time_step
440 while t_future < t_stop:
441 self.run_one_step()
442 t_future += self.solver.time_step
443
444 self.pbar.stand_by(self.solver.current_time)
445
447 """Run the MBDyn first step. The reason of that method is in
448 the MBDyn source code, the first step is run separately."""
449 if not self.has_run_init:
450 self.init()
451
452
453
454 self.save()
455
456 self.update()
457 self.solver.run_first_step()
458 self.save()
459
460 self.pbar = ProgressBar(self.solver.final_time + \
461 self.solver.time_step)
462 self.cpu_tstart = time.clock()
463 self.pbar.update(self.solver.current_time)
464 self._has_run_first_step = True
465
467 """Run one MBDyn integration step. The L{update} method
468 is called before a new time step. The L{save} method is called
469 after a time step. The L{final_action} is called when the
470 integration is finished."""
471 self.pbar.update(self.solver.current_time)
472 self.update()
473 solver_status = self.solver.run_one_step()
474 if solver_status == "stop":
475 self.pbar.destroy()
476 self._output_final_message()
477 self.final_action()
478 self.continue_iter = False
479 else:
480 self.save()
481
483 """Output the MBDyn final message. This step could not be any more
484 done in C++ because the progress bar needed to be stopped before
485 writing a message.
486 """
487 cpu_tstop = time.clock()
488 cpu_time = cpu_tstop - self.cpu_tstart
489 print "End of simulation at time ", self.solver.current_time, \
490 " after ", self.solver.step_nb, " steps"
491 print "total iterations: ", self.solver.total_iter_nb
492 print "total Jacobians: ", self.solver.jacobians_nb
493 print "total error: ", self.solver.total_error
494 print "CPU time consumed: ", cpu_time, " seconds"
495