wawi 0.0.1__py3-none-any.whl → 0.0.3__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
wawi/abq.py ADDED
@@ -0,0 +1,1128 @@
1
+ import numpy as np
2
+ import pdb
3
+
4
+ from abaqus import *
5
+ from abaqus import session
6
+ from abaqusConstants import *
7
+ import __main__
8
+ import section
9
+ import regionToolset
10
+ import displayGroupMdbToolset as dgm
11
+ import step
12
+ import part
13
+ import material
14
+ import assembly
15
+ import interaction
16
+ import load
17
+ import mesh
18
+ import optimization
19
+ import job
20
+ import sketch
21
+ import visualization
22
+ import xyPlot
23
+ import displayGroupOdbToolset as dgo
24
+ import connectorBehavior
25
+ import symbolicConstants
26
+ import odbAccess
27
+ import shutil
28
+
29
+ import csv
30
+ from copy import deepcopy
31
+
32
+ import numpy as np
33
+ import os
34
+
35
+ from .general import merge_tr_phi
36
+
37
+ '''
38
+ Abaqus interaction module
39
+ '''
40
+
41
+ ## Functions to retrieve data from ODB
42
+
43
+ def modalparameters(frequency_step):
44
+ '''
45
+ Output the modal parameters from frequency step of current output database.
46
+
47
+ Parameters
48
+ -------------
49
+ frequency_step : str
50
+ name of step containing the modal results (frequency step)
51
+
52
+ Returns
53
+ --------------
54
+ f : float
55
+ numpy array with undamped natural frequencies in Hz of all modes computed
56
+ m : float
57
+ numpy array with modal mass for all modes computed
58
+ '''
59
+
60
+ odb = get_db('odb')
61
+ history_region_key = odb.steps[frequency_step].historyRegions.keys()[0]
62
+
63
+ ftemp = odb.steps[frequency_step].historyRegions[history_region_key].historyOutputs['EIGFREQ'].data
64
+ f = np.array([x[1] for x in ftemp])
65
+
66
+ if 'GM' in odb.steps[frequency_step].historyRegions[history_region_key].historyOutputs.keys():
67
+ mtemp = odb.steps[frequency_step].historyRegions[history_region_key].historyOutputs['GM'].data
68
+ m = np.array([x[1] for x in mtemp])
69
+ else:
70
+ m = np.ones(np.shape(f)) #if no GM field is available, mass normalization is assumed used on eigenvalues
71
+ return f, m
72
+
73
+
74
+ def modeshapes_from_region(regionobjs, frequency_step, field_outputs):
75
+ """
76
+ Get modes (shape, frequency and modal mass) from "Frequency step" (eigenvalue analysis) in active Abaqus ODB.
77
+
78
+ Args:
79
+ regionobjs: Abaqus region objects in list
80
+ frequency_step: name of frequency step
81
+ field_outputs: list of strings with field output quantities, e.g., ['U', 'UR']
82
+ Returns:
83
+ phi: mode shape transformation matrix, ordered as NumPy matrices in list for each specified outputs
84
+ f: undamped natural frequencies
85
+ m: modal mass
86
+ output_dict: dictionary to access correct index in output phi
87
+
88
+ AAJ / Knut Andreas Kvaale, 2017
89
+ Further developed NTNU / Knut Andreas Kvaale, 2018
90
+ """
91
+ odb = get_db('odb')
92
+
93
+ if odb.steps[frequency_step].domain != MODAL: #MODAL is a variable in abaqusConstants
94
+ raise TypeError('Type of input step is not modal!')
95
+
96
+ Nmodes = len(odb.steps[frequency_step].frames)-1
97
+ phi = [None]*len(field_outputs)
98
+
99
+ for iout, field_output in enumerate(field_outputs):
100
+ Ndofs, point_ranges, dof_ranges = count_region(regionobjs, field_output, odb.steps[frequency_step].frames[0])
101
+ phio = np.zeros([np.sum(Ndofs), Nmodes])
102
+ foobj0 = odb.steps[frequency_step].frames[0].fieldOutputs[field_output]
103
+
104
+ for ix, regionobj in enumerate(regionobjs):
105
+ current_dof_range = np.arange(dof_ranges[ix], dof_ranges[ix+1])
106
+
107
+ for mode in range(0, Nmodes):
108
+ foobj = odb.steps[frequency_step].frames[mode+1].fieldOutputs[field_output]
109
+ phio[:, mode] = np.reshape((np.array([v.data for v in foobj.getSubset(region=regionobj).values])), [np.sum(Ndofs)])
110
+
111
+ phi[iout] = phio
112
+
113
+ return phi
114
+
115
+
116
+ def modeshapes_from_nodelist(node_labels, frequency_step, field_outputs):
117
+ """
118
+ Get mode shapes from "Frequency step" (eigenvalue analysis) in active Abaqus ODB.
119
+
120
+ Args:
121
+ node_labels:
122
+ frequency_step:
123
+ field_outputs:
124
+ Returns:
125
+ phi: mode shape transformation matrix, ordered as NumPy matrices in list for each specified outputs
126
+
127
+ NTNU / Knut Andreas Kvaale, 2018
128
+ """
129
+ odb = get_db('odb')
130
+
131
+ if odb.steps[frequency_step].domain != MODAL: #MODAL is a variable in abaqusConstants
132
+ raise TypeError('Type of input step is not modal!')
133
+
134
+ Nnodes = len(node_labels)
135
+ Nmodes = len(odb.steps[frequency_step].frames) - 1
136
+ phi = [None]*len(field_outputs)
137
+ basedisp = [None]*len(field_outputs)
138
+
139
+ for iout, field_output in enumerate(field_outputs):
140
+ foobj0 = odb.steps[frequency_step].frames[0].fieldOutputs[field_output]
141
+
142
+ Ndofs = len(foobj0.values[0].data)
143
+ phio = np.zeros([Ndofs*Nnodes, Nmodes])
144
+
145
+ # Get correct data indices to get correct order (as given in node_labels)
146
+ all_nodes = [value.nodeLabel for value in foobj0.values]
147
+ data_indices = [None]*Nnodes
148
+
149
+ for ix, node in enumerate(node_labels):
150
+ data_indices[ix] = all_nodes.index(node)
151
+
152
+ basedisp[iout] = np.array([foobj0.values[data_ix].data for data_ix in data_indices]).flatten()
153
+
154
+ for mode in range(0, Nmodes):
155
+ foobj = odb.steps[frequency_step].frames[mode+1].fieldOutputs[field_output]
156
+ phio[:, mode] = np.array([foobj.values[data_ix].data for data_ix in data_indices]).flatten()
157
+
158
+ phi[iout] = phio
159
+
160
+ return phi, basedisp
161
+
162
+
163
+ def modeshapes_from_elementlist(element_labels, frequency_step, field_outputs):
164
+ """
165
+ Get mode shape from "Frequency step" (eigenvalue analysis) in active Abaqus ODB.
166
+
167
+ Args:
168
+ node_labels:
169
+ frequency_step:
170
+ field_outputs:
171
+ Returns:
172
+ phi: mode shape transformation matrix, ordered as NumPy matrices in list for each specified outputs
173
+
174
+ NTNU / Knut Andreas Kvaale, 2018
175
+ """
176
+ odb = get_db('odb')
177
+
178
+ if odb.steps[frequency_step].domain != MODAL: #MODAL is a variable in abaqusConstants
179
+ raise TypeError('Type of input step is not modal!')
180
+
181
+
182
+ Nmodes = len(odb.steps[frequency_step].frames) - 1
183
+ phi = [None]*len(field_outputs)
184
+ integration_points = [None]*len(field_outputs)
185
+
186
+ for iout, field_output in enumerate(field_outputs):
187
+ foobj0 = odb.steps[frequency_step].frames[0].fieldOutputs[field_output]
188
+ Ndofs = len(foobj0.values[0].data)
189
+
190
+ # Get correct data indices to get correct order (as given in node_labels)
191
+ all_elements = [value.elementLabel for value in foobj0.values]
192
+ all_integration_points = [value.integrationPoint for value in foobj0.values]
193
+
194
+ Nintpoints = len(element_labels) # number of integration points (same element label might appear multiple times if multiple integration points in element)
195
+ phio = np.zeros([Ndofs*Nintpoints, Nmodes])
196
+
197
+ data_indices = [None]*Nintpoints
198
+
199
+ for ix, element in enumerate(element_labels):
200
+ data_indices[ix] = all_elements.index(element)
201
+
202
+ for mode in range(0, Nmodes):
203
+ foobj = odb.steps[frequency_step].frames[mode+1].fieldOutputs[field_output]
204
+ phio[:, mode] = np.array([foobj.values[data_ix].data for data_ix in data_indices]).flatten()
205
+
206
+ integration_points[iout] = [all_integration_points[ix] for ix in data_indices]
207
+ phi[iout] = phio
208
+
209
+
210
+ return phi, integration_points
211
+
212
+
213
+ def modeshapes_from_set_xydata(field_output, components, output_position, instance_name, set_name, region_type):
214
+ """
215
+ Get mode shapes from "Frequency step" (eigenvalue analysis) in active Abaqus ODB from specified sets.
216
+
217
+ Args: NOT FINISHED
218
+ field_output:
219
+ components:
220
+ data_position:
221
+ output_position:
222
+ set_name:
223
+ region_type:
224
+ Returns:
225
+ phi: mode shape transformation matrix (Numpy array)
226
+
227
+ NTNU / Knut Andreas Kvaale, 2018
228
+ """
229
+
230
+ set_names = [(instance_name + '.' +set_name)]
231
+
232
+ odb = get_db('odb')
233
+ n_components = len(components)
234
+ xy_data = [None]*n_components
235
+
236
+ if region_type == 'element':
237
+ data_position = INTEGRATION_POINT
238
+ elif region_type == 'node':
239
+ data_position = NODAL
240
+
241
+ if output_position == 'element':
242
+ output_position = ELEMENT_NODAL
243
+
244
+ for ix, component in enumerate(components):
245
+ refinement = [[COMPONENT, component]]
246
+ variable = [[field_output, data_position, refinement]]
247
+
248
+ if region_type == 'element':
249
+ xy_data[ix] = session.xyDataListFromField(odb=odb, outputPosition=output_position, variable=variable, elementSets=set_names)
250
+ else:
251
+ xy_data[ix] = session.xyDataListFromField(odb=odb, outputPosition=output_position, variable=variable, nodeSets=set_names)
252
+
253
+ n_elements = len(xy_data[0])
254
+ n_modes = len(xy_data[0][0])
255
+
256
+ phi = np.zeros([n_components*n_elements, n_modes])
257
+ for compix, component in enumerate(xy_data):
258
+ for elix, element in enumerate(component):
259
+ for mode in range(0, n_modes):
260
+ phi[elix*n_components + compix, mode] = element[mode][1]
261
+
262
+
263
+ return phi, xy_data
264
+
265
+ ## MODIFY ODB OR MDB
266
+ def set_view_variable(var, component):
267
+ """
268
+ Set a new view variable and component in current odb session.
269
+
270
+ Args:
271
+ var: variable name
272
+ component: component to display
273
+
274
+ NTNU / Knut Andreas Kvaale, 2018
275
+ """
276
+ position = {NODAL}
277
+ session.viewports['Viewport: 1'].odbDisplay.setPrimaryVariable(variableLabel=var, outputPosition=NODAL, refinement=(COMPONENT, component),)
278
+
279
+
280
+ def get_db(db_type):
281
+ """
282
+ Return the current database (either a model or an odb object).
283
+
284
+ If a model db is wanted and no model is active, the model in the mdb is selected regardless,
285
+ as long as there is only one model open in the mdb. If no database fits the requirements, None is returned.
286
+
287
+ Args:
288
+ db_type: 'odb' or 'model'
289
+ Returns:
290
+ db: database
291
+
292
+ NTNU / Knut Andreas Kvaale, 2018
293
+ """
294
+ if db_type is 'model' or db_type is 'mdb':
295
+ if not session_is_odb():
296
+ db = mdb.models[session.viewports['Viewport: 1'].displayedObject.modelName]
297
+ elif len(mdb.models.keys()) is 1:
298
+ db = mdb.models[mdb.models.keys()[0]]
299
+ elif len(mdb.models.keys()) > 1:
300
+ raise AttributeError('No model is not active, and more than one model is available in model database. Impossible to select correct.')
301
+ else:
302
+ db = None
303
+ else:
304
+ if session_is_odb():
305
+ db = session.viewports[session.currentViewportName].displayedObject
306
+ else:
307
+ db = None
308
+
309
+ return db
310
+
311
+
312
+
313
+ ## MODIFY ODB
314
+ def unlock_odb():
315
+ """
316
+ Unlock current ODB file.
317
+
318
+ Returns:
319
+ odb: database (odb) object
320
+
321
+ NTNU / Knut Andreas Kvaale, 2018
322
+ """
323
+ odb = session.viewports[session.currentViewportName].displayedObject
324
+
325
+ if odb.isReadOnly:
326
+ load_path = odb.path
327
+ odb.close()
328
+ odb = odbAccess.openOdb(load_path, readOnly=False)
329
+ session.viewports['Viewport: 1'].setValues(displayedObject=session.odbs[load_path])
330
+
331
+ return odb
332
+
333
+
334
+ def copy_and_unlock_odb():
335
+ """
336
+ Copy and unlock current ODB file.
337
+
338
+ Returns:
339
+ odb: database (odb) object
340
+
341
+ NTNU / Knut Andreas Kvaale, 2018
342
+ """
343
+ odb = session.viewports[session.currentViewportName].displayedObject
344
+ old_file_path = odb.path
345
+ new_file_path = odb.path.split('.odb')[0]+'_org.odb'
346
+
347
+ shutil.copyfile(old_file_path, new_file_path) #copy the old file
348
+
349
+ odb.close()
350
+ odb = odbAccess.openOdb(old_file_path, readOnly=False)
351
+ session.viewports['Viewport: 1'].setValues(displayedObject=session.odbs[old_file_path])
352
+
353
+ return odb
354
+
355
+
356
+ def session_is_odb():
357
+ """
358
+ Check if current session is ODB.
359
+
360
+ Returns:
361
+ is_odb: boolean indicating if the session is odb or not
362
+
363
+ NTNU / Knut Andreas Kvaale, 2018
364
+ """
365
+ is_odb =(('session' in locals() or 'session' in globals()) and
366
+ session.viewports['Viewport: 1'].displayedObject is not None and
367
+ hasattr(session.viewports['Viewport: 1'].displayedObject, 'jobData'))
368
+
369
+ return is_odb
370
+
371
+
372
+ def save_and_reopen_odb():
373
+ """
374
+ Save and reopen database (odb) as read-only.
375
+
376
+ Returns:
377
+ odb: odb object
378
+
379
+ NTNU / Knut Andreas Kvaale, 2018
380
+ """
381
+ odb = get_db('odb')
382
+ odb.save()
383
+ load_path = odb.path
384
+ odb.close()
385
+
386
+ odb = odbAccess.openOdb(load_path, readOnly=True)
387
+
388
+ return odb
389
+
390
+
391
+ def add_response_step_from_modal(phi_response, field_outputs, modal_var, frequency_step, step_name, region_strings, region_type, instance_name, description):
392
+ """
393
+ Add an artificial step in Abaqus ODB for response data.
394
+
395
+ Args:
396
+ phi_response: phi of the requested response quantities (list with one matrix for each response quantities)
397
+ field_outputs: names of field output variables
398
+ modal_var: covariance matrix for the generalized (modal) DOFs
399
+ frequency_step: name of the new artificial step_name
400
+ step_name: node set name or region object that define what nodes / DOFs phi refers to
401
+ regionobjs: Abaqus region objects in list
402
+ instance_name: name of the instance
403
+ description: frame description
404
+
405
+ NTNU / Knut Andreas Kvaale, 2018
406
+ """
407
+
408
+ odb = copy_and_unlock_odb()
409
+ regionobjs = str2region(instance_name, region_strings, region_type, 'odb')
410
+ instance = odb.rootAssembly.instances[instance_name]
411
+
412
+ step_data = odb.Step(name=step_name, description='Response step', domain=TIME, timePeriod=0)
413
+ frame = step_data.Frame(incrementNumber=0, description='Response', frameValue=0)
414
+
415
+ type_dict = {'SF': [TENSOR_3D_SURFACE, INTEGRATION_POINT, 'Section forces'], 'SM': [TENSOR_3D_SURFACE, INTEGRATION_POINT, 'Section moments'], 'U': [VECTOR, NODAL, 'Spatial displacement'], 'UR': [VECTOR, NODAL, 'Rotational displacement'] }
416
+
417
+ for ix, field_output in enumerate(field_outputs):
418
+ foobj_ref = odb.steps[frequency_step].frames[0].fieldOutputs[field_output]
419
+ phi = phi_response[ix]
420
+ region_type = type_dict[field_output][1]
421
+ comps = len(odb.steps[frequency_step].frames[0].fieldOutputs[field_output].componentLabels)
422
+
423
+ sigma = np.sqrt(np.sum((np.dot(phi, modal_var) * phi), axis=1)) # Calculate sigma (square root of covariance matrix) from modal coordinates
424
+ sigma_comp = np.reshape(sigma, [-1, 3]).astype('float')
425
+ data = [list(this) for this in sigma_comp]
426
+
427
+ foobj = frame.FieldOutput(name=field_output, description=type_dict[field_output][2], type=type_dict[field_output][0], validInvariants=())
428
+
429
+ N = len(odb.steps[frequency_step].frames[0].fieldOutputs[field_output].values)
430
+ Ndofs, point_ranges, dof_ranges = count_region(regionobjs, field_output, odb.steps[frequency_step].frames[0])
431
+
432
+ for regix,regionobj in enumerate(regionobjs):
433
+ good_ix, good_entries = good_element_ix(foobj_ref, regionobj)
434
+ point_range = range(point_ranges[regix],point_ranges[regix+1])
435
+
436
+ foobj.addData(position=region_type, instance=instance, labels=good_entries, data=data)
437
+
438
+ step_data.setDefaultField(foobj)
439
+
440
+ odb = save_and_reopen_odb()
441
+
442
+ return odb
443
+
444
+
445
+ def add_std_to_frame(odb, frame, instance_name, modal_var, phi, regionobj, field_output, reference_step):
446
+ '''
447
+ Under development. Not verified.
448
+ '''
449
+ if odb.isReadOnly:
450
+ raise TypeError('ODB is read only. Unable to add data.')
451
+
452
+ type_dict = {'SF': [TENSOR_3D_SURFACE, INTEGRATION_POINT, 'Section forces'], 'SM': [TENSOR_3D_SURFACE, INTEGRATION_POINT, 'Section moments'], 'U': [VECTOR, NODAL, 'Spatial displacement'], 'UR': [VECTOR, NODAL, 'Rotational displacement'] }
453
+ foobj_ref = odb.steps[reference_step].frames[0].fieldOutputs[field_output]
454
+
455
+ region_type = type_dict[field_output][1]
456
+ comps = len(odb.steps[reference_step].frames[0].fieldOutputs[field_output].componentLabels)
457
+
458
+ sigma = np.sqrt(np.sum((np.dot(phi, modal_var) * phi), axis=1)) # Calculate sigma (square root of covariance matrix) from modal coordinates
459
+ sigma_comp = np.reshape(sigma, [-1, comps]).astype('float')
460
+ data = [list(this) for this in sigma_comp]
461
+
462
+ # If already exists, don't create new, but assign to that.
463
+ if field_output not in frame.fieldOutputs.keys():
464
+ foobj = frame.FieldOutput(name=field_output, description=type_dict[field_output][2], type=type_dict[field_output][0], validInvariants=())
465
+ else:
466
+ foobj = frame.fieldOutputs[field_output]
467
+
468
+ N = len(odb.steps[reference_step].frames[0].fieldOutputs[field_output].values)
469
+ good_ix, good_entries = good_element_ix(foobj_ref, regionobj)
470
+ instance = odb.rootAssembly.instances[instance_name]
471
+
472
+ foobj.addData(position=region_type, instance=instance, labels=good_entries, data=data)
473
+ step_data.setDefaultField(foobj)
474
+
475
+
476
+
477
+ def add_complex_mode_step(phi, eigvals, instance_name, step_name, region):
478
+ """
479
+ Add an artificial step in Abaqus ODB for complex modes.
480
+
481
+ Args:
482
+ phi: complex eigenvector matrix
483
+ eigvals: complex eigenvalues
484
+ instance_name: name of the instance
485
+ step_name: name of the new artificial step_name
486
+ regionobj: Abaqus region object
487
+
488
+ Knut Andreas Kvaale, 2018
489
+ """
490
+
491
+ odb = unlock_odb()
492
+ complex_step = odb.Step(name=step_name, description='Complex modes', domain=MODAL)
493
+ frame0 = complex_step.Frame(incrementNumber=0, description='Base state', frameValue=0)
494
+ frame_data = frame0.FieldOutput(name='U', description='Spatial displacement', type=VECTOR, validInvariants=(MAGNITUDE,))
495
+ instance = odb.rootAssembly.instances[instance_name]
496
+
497
+ for m, lambdam in enumerate(eigvals):
498
+ phim = np.reshape(phi[:, m], (-1, 3)).astype('float')
499
+ xim = -np.real(lambdam)/abs(lambdam)
500
+
501
+ freqm_ud_rad = abs(lambdam)
502
+ freqm_d_rad = abs(np.imag(lambdam))
503
+ freqm_ud_Hz = freqm_ud_rad/(2*np.pi)
504
+
505
+ periodm_ud = 2*np.pi/abs(lambdam)
506
+
507
+ description_m = 'Mode ' + str(m+1) + ': f = ' + str(freqm_ud_Hz) + 'Hz | om = ' + str(freqm_ud_rad) + 'rad/s | T = ' + str(periodm_ud) + 's | xi = ' + str(xim*100) + '%'
508
+
509
+ frame_m = complex_step.Frame(incrementNumber=m+1, description=description_m, frameValue=freqm_ud_Hz)
510
+ frame_data = frame_m.FieldOutput(name='U', description='Spatial displacement', type=VECTOR, validInvariants=(MAGNITUDE,))
511
+ nodelabels = np.array([node.label for node in regionobj.nodes[0]]).astype('int')
512
+
513
+ frame_data.addData(position=NODAL, instance=instance, labels=nodelabels, data=np.real(phim), conjugateData=np.imag(phim))
514
+
515
+ odb.save()
516
+ load_path = odb.path
517
+ odb.close()
518
+ odb = odbAccess.openOdb(load_path, readOnly=True)
519
+
520
+ ## MODIFY MDB
521
+ def mass_and_stiffness_input(stiffness,mass,pontoon_set_names,pont_nodes,trans_mats,filename):
522
+ pontoons = len(pontoon_set_names)
523
+ if len(pont_nodes) != pontoons or len(trans_mats)!=pontoons:
524
+ raise ValueError('Mismatch between dimensions for input variables: pontoon_set_names, pont_nodes and trans_mats')
525
+ f = open(filename, 'w')
526
+
527
+ for pontoon in range(0,pontoons):
528
+ f.write('********************************PONTOON NUMBER {0} ************************************* \n'.format(str(pontoon+1)))
529
+ f.write('*USER ELEMENT, LINEAR, NODES=1, UNSYM, TYPE=U{0}00 \n'.format(str(pontoon+1))) # Defines a linear user element
530
+ f.write('1, 2, 3, 4, 5, 6 \n') # The element has one node with 6 DOFS
531
+
532
+ T = trans_mats[pontoon]
533
+
534
+ K = np.dot(np.dot(T.transpose(), stiffness),T)
535
+ M = np.dot(np.dot(T.transpose(), mass),T)
536
+
537
+ f.write('*MATRIX, TYPE=MASS \n') # Defines the mass matrix in GLOBAL coordinate system
538
+ for n in range(0,6):
539
+ string1 = ','.join(map(str, M[n, 0:4]))
540
+ string2 = ','.join(map(str, M[n, 4:6]))
541
+ f.write(string1 + '\n' + string2 +'\n')
542
+
543
+ f.write('*MATRIX, TYPE=STIFFNESS \n')
544
+ for n in range(0,6):
545
+ string1 = ','.join(map(str, K[n, 0:4]))
546
+ string2 = ','.join(map(str, K[n, 4:6]))
547
+ f.write(string1 + '\n' + string2 +'\n')
548
+
549
+ f.write('*ELEMENT, TYPE=U{0}00, ELSET={1} \n'.format(str(pontoon+1),pontoon_set_names[pontoon])) #Introduce one user element into the FE model
550
+ f.write('800{0}, {1} \n'.format(str(pontoon+1),pont_nodes[pontoon])) #Numbering elements as 8001,8002,...,8007, followed by first node number forming the element
551
+ f.write('*UEL PROPERTY, ELSET={0} \n'.format(pontoon_set_names[pontoon]))
552
+
553
+
554
+ def update_input(freq,wadam_file,input_file,pontoon_set_names,pont_nodes,trans_mats):
555
+ from .io import import_wadam_mat
556
+ static_mass, stiffness, added_mass, damping, frequency = import_wadam_mat(wadam_file)
557
+ mass = freq_sysmat(added_mass,frequency,freq)+static_mass
558
+ mass_and_stiffness_input(stiffness,mass,pontoon_set_names,pont_nodes,trans_mats,input_file)
559
+ print('Input file '+ input_file + ' is modified to correspond to added mass at f = ' + str(freq) + ' Hz.')
560
+
561
+
562
+ def imperfection_input(node_labels, displacement_vector, input_file=None, rotations=False):
563
+
564
+ d = np.array(displacement_vector)
565
+
566
+ if rotations is True:
567
+ n_nodes = len(d)/6
568
+ d_trans = np.zeros([n_nodes*3])
569
+ for node in range(0, n_nodes):
570
+ d_trans[node*3:node*3+3] = d[node*6:node*6+3]
571
+
572
+ d = d_trans
573
+ else:
574
+ n_nodes = len(d)/3
575
+
576
+ mat = np.hstack([np.array(node_labels)[:, np.newaxis], d.reshape(-1, 3)])
577
+
578
+ if input_file != None:
579
+ open(input_file, 'w').close()
580
+
581
+ with open(input_file, 'a') as f:
582
+ f.write('*IMPERFECTION \n')
583
+ np.savetxt(f, mat, delimiter=',', fmt='%i,%.8e,%.8e,%.8e')
584
+
585
+ return mat
586
+
587
+
588
+ def add_input_file(model, input_file_path, pos, target_string=None, relative_pos=0):
589
+
590
+ if target_string != None:
591
+ pos = model.keywordBlock.sieBlocks.index(target_string)
592
+
593
+ model.keywordBlock.insert(pos+relative_pos, '*INCLUDE, INPUT={0}'.format(input_file_path))
594
+
595
+
596
+ def add_springs(assem, Kh, region, name):
597
+
598
+ ON = symbolicConstants.AbaqusBoolean(1)
599
+
600
+ Kpos = (Kh+abs(Kh))/2
601
+ Krem = (Kh-abs(Kh))/2
602
+
603
+ for dof in range(2, 5):
604
+ if Kpos[dof, dof] != 0:
605
+ assem.engineeringFeatures.SpringDashpotToGround(name=name+'_K%i%i' % (dof+1, dof+1), region=region, orientation=None, dof=dof+1, springBehavior=ON, springStiffness=Kpos[dof, dof])
606
+
607
+ return Krem
608
+
609
+
610
+ def add_inertia(assem, M0, region, name, specify_rot=False):
611
+ if specify_rot is True:
612
+ assem.engineeringFeatures.PointMassInertia(alpha=0.0, composite=0.0, i11=M0[3, 3], i12=M0[3, 4], i13=M0[3, 5], i22=M0[4, 4], i23=M0[4, 5],
613
+ i33=M0[5, 5], mass1=M0[0, 0], mass2=M0[1, 1], mass3=M0[2, 2], name=name+'_M0', region=region)
614
+ comps = range(0, 6)
615
+ elif specify_rot is False:
616
+ assem.engineeringFeatures.PointMassInertia(alpha=0.0, composite=0.0, mass1=M0[0, 0], mass2=M0[1, 1], mass3=M0[2, 2], name=name+'_M0', region=region)
617
+ comps = range(0, 3)
618
+
619
+ Mrem = deepcopy(M0)
620
+
621
+ for comp in comps:
622
+ Mrem[comp, comp] = 0
623
+
624
+ return Mrem
625
+
626
+
627
+ #%% USEFUL FUNCTIONS FOR DEALING WITH REGIONS IN DATABASE
628
+ def count_region(regionobjs, field_output, frame):
629
+ """
630
+ Count the number of DOFs and points in the specified region objects for given field output and frame object.
631
+
632
+ Args:
633
+ regionobjs: list of region objects to query
634
+ field_output: string specifying field output
635
+ frame: frame object (from where fieldOutputs field is accessible)
636
+ Returns:
637
+ Ndofs: number of DOFs for each region (list)
638
+ point_ranges: point/node ranges for each region (list of lists)
639
+ dof_ranges: dof ranges for each region (list of lists)
640
+
641
+ NTNU / Knut Andreas Kvaale, 2018
642
+ """
643
+ odb = get_db('odb')
644
+
645
+ Npoints = [len(frame.fieldOutputs[field_output].getSubset(region=regionobj).values) for regionobj in regionobjs]
646
+ Ndofs = np.dot(Npoints, len(frame.fieldOutputs[field_output].componentLabels))
647
+
648
+ dof_ranges = np.cumsum(np.append([0], Ndofs))
649
+ point_ranges = np.cumsum(np.append([0], Npoints))
650
+
651
+ return Ndofs, point_ranges, dof_ranges
652
+
653
+
654
+ def good_element_ix(foobj, regionobj):
655
+ """
656
+ Get the indices of the good (??) elements.
657
+
658
+ Args:
659
+ foobj: field object
660
+ regionobj: region object
661
+ Returns:
662
+ good_ix: ?
663
+ good_entries: ?
664
+
665
+ NTNU / Knut Andreas Kvaale, 2018
666
+ """
667
+ foobj_values = foobj.getSubset(region=regionobj).values
668
+ region_type = obtain_region_types([regionobj])[0]
669
+
670
+ if region_type is 'elements':
671
+ rootobj = regionobj.elements
672
+ label_string = 'elementLabel'
673
+ elif region_type is 'nodes':
674
+ rootobj = regionobj.nodes
675
+ label_string = 'nodeLabel'
676
+
677
+ if type(rootobj) is tuple:
678
+ rootobj = rootobj[0]
679
+
680
+ good_entries = [getattr(val, label_string) for val in foobj_values]
681
+ all_entries = [obj.label for obj in rootobj]
682
+
683
+ good_ix = [all_entries.index(this_entry) for this_entry in good_entries]
684
+
685
+ return good_ix, good_entries
686
+
687
+
688
+ def obtain_region_types(regionobjs):
689
+ """
690
+ Get the region types of list of region objects.
691
+
692
+ Args:
693
+ regionobjs: list of region objects
694
+ Returns:
695
+ region_type: list of region types
696
+
697
+ NTNU / Knut Andreas Kvaale, 2018
698
+ """
699
+ elementsets = [regionobj.nodes is None for regionobj in regionobjs] # true if regionobjects are element sets
700
+ settypedict = {False: 'nodes', True: 'elements'}
701
+ region_type = [settypedict[elementset] for elementset in elementsets]
702
+
703
+ return region_type
704
+
705
+
706
+ def str2region(instance_name, setnames, region_type, db_type, *args):
707
+ """
708
+ Construct a region object from a string defining the set name or a region object.
709
+
710
+ Args:
711
+ instance_name: string defining the set name (either node or element set) or a region object
712
+ setnames: name of set asked for
713
+ region_type: type of set ('elements' or 'nodes')
714
+ db_type: 'odb' or 'model'
715
+ Optional args:
716
+ db: database object, either mdb.model[...] or session.openOdb(...) - will get from viewport 1 if not given
717
+ Returns:
718
+ regionobjs: region objects
719
+
720
+ AAJ / Knut Andreas Kvaale, 2017
721
+ Further developed NTNU / Knut Andreas Kvaale, 2018
722
+ """
723
+
724
+ is_assembly = instance_name is None
725
+
726
+ set_type = settype(region_type, db_type)
727
+ standard_sets = {'nodes': [' ALL NODES'], 'elements': [' ALL ELEMENTS']}
728
+
729
+ if setnames is None:
730
+ setnames = standard_sets[region_type]
731
+
732
+ if len(args)==1: # a db has been input
733
+ db = args[0]
734
+ isodb = hasattr(db,'jobData') #check if the input db is reffering to result/odb or model
735
+
736
+ else:
737
+ db = get_db(db_type)
738
+
739
+ if db is None:
740
+ raise TypeError('The database is empty. Please input a database object, or input parameters that matches one. Remember that odbs have to be active to get the db automatically!')
741
+
742
+ if is_assembly: # Instance name is given
743
+ regroot = db.rootAssembly
744
+ else:
745
+ regroot = db.rootAssembly.instances[instance_name]
746
+
747
+ regionobjs = [None] * np.size(setnames)
748
+
749
+ for ix,thisname in enumerate(setnames):
750
+ regionobjs[ix] = getattr(regroot, set_type)[thisname]
751
+
752
+ return regionobjs
753
+
754
+
755
+ def region2nodes(regionobj, sortfun=None):
756
+ """
757
+ Give node labels (indices) of nodes in specified node set(s).
758
+
759
+ Args:
760
+ regionobj: region object to query for node labels
761
+
762
+ Optional args:
763
+ sortfun: function with three inputs (1: x, 2: y, 3:z) to sort nodes by
764
+ examples: sortfun = lambda x, y, z: -np.arctan2(y,x)
765
+ sortfun = lambda x, y, z: x
766
+
767
+ Returns:
768
+ node_labels: list with nodelabels
769
+
770
+ NTNU / Knut Andreas Kvaale, 2018
771
+ """
772
+
773
+ set_name = regionobj.__repr__().split("ets[")[1].split("'")[1]
774
+
775
+ if len(np.shape(regionobj.nodes))>1:
776
+ nodes = regionobj.nodes[0]
777
+ else:
778
+ nodes = regionobj.nodes
779
+
780
+ node_labels = np.array([node.label for node in nodes])
781
+ node_coordinates = np.array([node.coordinates for node in nodes])
782
+
783
+ if sortfun != None:
784
+ vals = sortfun(x=node_coordinates[:,0], y=node_coordinates[:,1], z=node_coordinates[:,2])
785
+ sort_ix = np.argsort(vals)
786
+ node_labels = node_labels[:, sort_ix]
787
+ node_coordinates = node_coordinates[sort_ix, :]
788
+
789
+ return node_labels, node_coordinates
790
+
791
+ def region2elnodes(regionobj, avoid_central_nodes=True, db_type='odb'):
792
+ """
793
+ Give node labels (indices) for each node in specified element set.
794
+
795
+ Args:
796
+ regionobj: region object to query for labels
797
+
798
+ Returns:
799
+ element_labels: the labels (indices) of the elements in list
800
+ element_node_indices: the labels (indices) of the ndoes in each element; list of lists
801
+ node_labels: all the nodes labels (indices) in a flattened list
802
+ node_coordinates: node coordinates for each element (list of lists)
803
+
804
+ NTNU / Knut Andreas Kvaale, 2018
805
+ """
806
+
807
+ db = get_db(db_type)
808
+ objstr = regionobj.__repr__()
809
+ if 'instances' in objstr:
810
+ instance_name = objstr.split(".instances['")[1].split("'].")[0]
811
+ else:
812
+ instance_name = None
813
+
814
+ if instance_name is None:
815
+ instance = db.rootAssembly
816
+ else:
817
+ instance = db.rootAssembly.instances[instance_name]
818
+
819
+ # Get the elements object root
820
+ if len(np.shape(regionobj.elements))>1:
821
+ elements = regionobj.elements[0]
822
+ else:
823
+ elements = regionobj.elements
824
+
825
+ # Get all element labels and corresponding connectivity (node labels)
826
+ element_labels = np.array([element.label for element in elements])
827
+ node_labels = [el.connectivity for el in elements]
828
+
829
+ if avoid_central_nodes:
830
+ node_labels = np.unique([item for sublist in node_labels for item in sublist[:1]+sublist[-1:]])
831
+ else:
832
+ node_labels = [item for sublist in node_labels for item in sublist]
833
+
834
+ element_matrix = None
835
+
836
+ return element_labels, node_labels, element_matrix
837
+
838
+
839
+ def get_element_matrix(element_labels=None): #if None is specified, full model is exported
840
+ pass
841
+
842
+ def get_node_matrix(node_labels=None): #if None is specified, full model is exported
843
+ pass
844
+
845
+ def region2elnodes_legacy(regionobjs, avoid_central_nodes=True):
846
+ """
847
+ Give node labels (indices) for each node in specified element set.
848
+
849
+ Args:
850
+ regionobjs: region objects to query for node labels
851
+
852
+ Returns:
853
+ element_labels: the labels (indices) of the elements in list
854
+ element_node_indices: the labels (indices) of the ndoes in each element; list of lists
855
+ node_labels: all the nodes labels (indices) in a flattened list
856
+ node_coordinates: node coordinates for each element (list of lists)
857
+
858
+ NTNU / Knut Andreas Kvaale, 2018
859
+ """
860
+
861
+ objstr = regionobjs.__repr__()
862
+ instance_name = objstr.split(".instances['")[1].split("'].")[0]
863
+
864
+ if '.odb' in objstr:
865
+ db = get_db('odb')
866
+ dbtype = 'odb'
867
+ else:
868
+ db = get_db('mdb')
869
+ dbtype = 'mdb'
870
+
871
+ # Get the elements object root
872
+ if len(np.shape(regionobjs.elements))>1:
873
+ elements = regionobjs.elements[0]
874
+ else:
875
+ elements = regionobjs.elements
876
+
877
+ # Get all element labels and corresponding connectivity (node labels)
878
+ element_labels = np.array([element.label for element in elements])
879
+
880
+ # Instance object
881
+ instance = db.rootAssembly.instances[instance_name]
882
+
883
+ # Full arrays labels and coordinates
884
+ all_node_labels = np.array([node.label for node in instance.nodes]).flatten([-1])
885
+ all_node_coords = np.array([node.coordinates for node in instance.nodes])
886
+
887
+ # Nodes belonging to all the elements
888
+ if dbtype is 'odb':
889
+ element_node_labels = [element.connectivity for element in elements]
890
+ else:
891
+ element_node_labels = [[all_node_labels[ix] for ix in element.connectivity] for element in elements]
892
+
893
+ if avoid_central_nodes:
894
+ element_node_labels = [[node_lb[0], node_lb[-1]] for node_lb in element_node_labels]
895
+
896
+ node_labels = np.unique(np.array(element_node_labels).flatten())
897
+
898
+ nodeixs = np.array([np.where(all_node_labels==node)[0] for node in node_labels]).flatten()
899
+ node_coordinates = all_node_coords[nodeixs, :]
900
+ element_node_indices = np.array([np.array([np.where(node_labels==node_label) for node_label in node_labels_for_element]).flatten() for node_labels_for_element in element_node_labels])
901
+
902
+ return element_labels, element_node_indices, node_labels, node_coordinates
903
+
904
+
905
+ #%% RETRIEVE THINGS FROM DATABASE
906
+ def element_orientations(element_labels, instance_name):
907
+ """
908
+ Provide transformation matrices describing the three unit vectors of the local CSYS of all elements in element_labels.
909
+
910
+ Args:
911
+ element_labels: element labels to query
912
+ instance_name: name of instance to find beam orientations
913
+
914
+ Returns:
915
+ element_orientations: array of numpy 2d-arrays with transformation matrices of all elements in element_labels
916
+
917
+ NTNU / Knut Andreas Kvaale, 2018
918
+ """
919
+ db_type = 'odb' # may consider mdb option later
920
+ db = get_db(db_type)
921
+
922
+ all_elements = db.rootAssembly.elementSets[' ALL ELEMENTS'].elements[0]
923
+ all_nodes = db.rootAssembly.nodeSets[' ALL NODES'].nodes[0]
924
+ all_element_labels = [value.label for value in all_elements]
925
+ all_node_labels = [value.label for value in all_nodes]
926
+ element_orientations = [None]*len(element_labels)
927
+
928
+ beam_orientations = db.rootAssembly.instances[instance_name].beamOrientations
929
+
930
+ for beam_orientation in beam_orientations:
931
+ bo_elements = [value.label for value in beam_orientation.region.elements]
932
+ for this_element_label in bo_elements:
933
+ if this_element_label in element_labels:
934
+ n1_temp = np.array(beam_orientation.vector)
935
+ node_labels = all_elements[all_element_labels.index(this_element_label)].connectivity
936
+
937
+ node_start_coor = all_nodes[all_node_labels.index(node_labels[0])].coordinates
938
+ node_end_coor = all_nodes[all_node_labels.index(node_labels[-1])].coordinates
939
+ t = (node_end_coor-node_start_coor)
940
+ t = t/np.linalg.norm(t)
941
+
942
+ n2 = np.cross(t, n1_temp)
943
+ n2 = n2/np.linalg.norm(n2)
944
+
945
+ n1 = np.cross(n2, t) #does this actually work?
946
+
947
+ element_orientations[np.where(element_labels == this_element_label)[0]] = np.array([t,n1,n2])
948
+
949
+ return element_orientations
950
+
951
+
952
+ def freq_sysmat(mat,freqs,freq):
953
+ """
954
+ Interpolate frequency dependent matrix, for given frequency value. !! Deprecated - use numpy functions directly instead !!
955
+
956
+ Args:
957
+ mat: 3D matrix (Numpy array)
958
+ freqs: frequency axis (Numpy array)
959
+ freq: selected frequency value (scalar)
960
+ Returns:
961
+ mat_sel: 2D matrix corresponding to queried frequency value (Numpy array)
962
+
963
+ NTNU / AAJ / Knut Andreas Kvaale, 2018
964
+ """
965
+ from .general import interp1z
966
+
967
+ if freq == []:
968
+ mat_sel = 0
969
+ else:
970
+ mat_sel = interp1z(freqs[:,0,0],mat,freq)
971
+ return mat_sel
972
+
973
+
974
+ def wind_set_data(set_strings, frequency_step, instance, db_type, field_outputs, mode_type='nodes', use_node_region_acronym=False):
975
+ # use_node_region_acronym: if True, a node set with identical name as the element set given in set_strings is picked and the nodes assumed to correspond to the element. If not the case, the element set is used to establish the nodes (and thus phi)
976
+ wind_element_regions = str2region(instance, set_strings, 'elements', db_type) # index 0 is girder, index 1 is columns
977
+
978
+ if use_node_region_acronym:
979
+ wind_node_regions = str2region(instance, set_strings, 'nodes', db_type)
980
+
981
+ element_labels = [None]*len(set_strings)
982
+ element_node_indices = [None]*len(set_strings)
983
+ node_labels = [None]*len(set_strings)
984
+ node_coordinates = [None]*len(set_strings)
985
+ phi_ae = [None]*len(set_strings)
986
+
987
+ for set_ix, set_string in enumerate(set_strings):
988
+ element_labels[set_ix], element_node_indices[set_ix], nl, nc = region2elnodes_legacy(wind_element_regions[set_ix])
989
+ if use_node_region_acronym:
990
+ nl, nc = region2nodes(wind_node_regions[set_ix])
991
+
992
+ node_labels[set_ix] = nl
993
+ node_coordinates[set_ix] = nc
994
+
995
+ # Establish modal transformation matrix, phi
996
+ if mode_type=='nodes':
997
+ for set_ix, set_string in enumerate(set_strings):
998
+ phi_ae_temp = modeshapes_from_nodelist(node_labels[set_ix], frequency_step, field_outputs)
999
+ phi_ae[set_ix] = merge_tr_phi(phi_ae_temp[0][0], phi_ae_temp[0][1])
1000
+ elif mode_type=='elements':
1001
+ for set_ix, set_string in enumerate(set_strings):
1002
+ phi_ae_temp, integration_points = modeshapes_from_elementlist(element_labels[set_ix], frequency_step, field_outputs)
1003
+ phi_ae[set_ix] = merge_tr_phi(phi_ae_temp[0], phi_ae_temp[1])
1004
+
1005
+ return element_labels, element_node_indices, node_labels, node_coordinates, phi_ae
1006
+
1007
+
1008
+
1009
+ def settype(region_type, db_type):
1010
+ """
1011
+ Define the string used to get set based on region type and database type.
1012
+
1013
+ Args:
1014
+ region_type: 'element' or 'node'
1015
+ db_type: 'odb' or 'mdb'
1016
+ Returns:
1017
+ set_string: string used to obtain set data from database object (odb or mdb)
1018
+
1019
+ NTNU / Knut Andreas Kvaale, 2018
1020
+ """
1021
+ if db_type is 'odb':
1022
+ if 'element' in region_type.lower():
1023
+ set_string = 'elementSets'
1024
+ elif 'node' in region_type.lower():
1025
+ set_string = 'nodeSets'
1026
+ else:
1027
+ raise TypeError('Wrong input!')
1028
+ elif db_type == 'mdb' or db_type == 'model':
1029
+ set_string = 'sets'
1030
+
1031
+ return set_string
1032
+
1033
+ #%% EXPORT THINGS
1034
+ def save_nodes_and_elements(folder, element_labels, element_node_indices, node_labels, node_coordinates, element_orientations=None, set_strings=None):
1035
+ for ix, element_labels_i in enumerate(element_labels):
1036
+ element_info = np.column_stack([element_labels[ix], element_node_indices[ix]])
1037
+ node_info = np.column_stack([node_labels[ix], node_coordinates[ix]])
1038
+ np.savetxt(os.path.join(folder, 'node_info_%i.dat' % (ix)), node_info)
1039
+ np.savetxt(os.path.join(folder, 'element_info_%i.dat' % (ix)), element_info)
1040
+
1041
+ if element_orientations:
1042
+ np.savetxt(os.path.join(folder, 'element_orientations_%i.dat' % (ix)), element_orientations)
1043
+
1044
+ if set_strings:
1045
+ np.savetxt(os.path.join(folder, 'node_and_element_sets.txt'), set_strings, fmt='%s', delimiter=',')
1046
+
1047
+
1048
+ def save_pontoon_info(folder, node_labels, node_coordinates, pontoon_labels=None, pontoon_angles=None):
1049
+ if pontoon_labels==None: # standard if no pontoon_labels are provided (integers!)
1050
+ pontoon_labels = np.linspace(1, len(node_labels), len(node_labels)).astype(int)
1051
+
1052
+ if pontoon_angles==None:
1053
+ pontoon_angles = np.zeros(len(node_labels)) #if no angles are given, output zero for all pontoon angles
1054
+
1055
+ pontooninfo = np.column_stack([pontoon_labels, node_coordinates, node_labels, pontoon_angles])
1056
+ np.savetxt(os.path.join(folder, 'pontoon_info.dat'), pontooninfo)
1057
+
1058
+
1059
+ def save_all_modal(folder, phi, suffix='', f=None, m=None, set_strings=None):
1060
+
1061
+ if isinstance(phi, list):
1062
+ for ix, phi_i in enumerate(phi):
1063
+ np.savetxt(os.path.join(folder, 'phi_%s_%i.dat' % (suffix, ix)), phi_i)
1064
+
1065
+ if set_strings:
1066
+ np.savetxt(os.path.join(folder, 'phi_%s_sets.txt' % (suffix)), set_strings, fmt='%s', delimiter=',')
1067
+
1068
+ elif isinstance(phi, np.ndarray):
1069
+ np.savetxt(os.path.join(folder, 'phi_%s_%i.dat' % (suffix, 0)), phi)
1070
+
1071
+ if f is not None:
1072
+ np.savetxt(os.path.join(folder, 'f.dat'), f)
1073
+ if m is not None:
1074
+ np.savetxt(os.path.join(folder, 'm.dat'), m)
1075
+
1076
+
1077
+ #%% ONLY DEBUGGED IN BRIGADE
1078
+ def mode2df(model, node_labels, phi, name, instance_name):
1079
+ nodes = tuple(np.repeat(node_labels,6).tolist())
1080
+ dofs = np.tile(np.arange(1,6+1), len(node_labels))
1081
+
1082
+ dofs_and_mags = np.empty([np.shape(dofs)[0],2])
1083
+ dofs_and_mags[:, 0::2] = dofs[:, np.newaxis]
1084
+ dofs_and_mags[:, 1::2] = phi[:, np.newaxis]
1085
+
1086
+ data = ((instance_name, 2, nodes, tuple(dofs_and_mags.flatten().tolist())),)
1087
+ df = model.DiscreteField(data=data, dataWidth=2, defaultValues=(0.0, 0.0, 0.0, 0.0, 0.0, 0.0), description='Mode displacement', fieldType=PRESCRIBEDCONDITION_DOF, location=NODES, name=name)
1088
+
1089
+ return df
1090
+
1091
+
1092
+ def apply_nodal_load(model, node_labels, step_name, loads, instance_name, prefix=''):
1093
+ instance = model.rootAssembly.instances[instance_name]
1094
+ all_node_labels = [node.label for node in instance.nodes]
1095
+ ndof = 6 # assumes 6 DOFs for all nodes - be aware!
1096
+ for node_ix, node_label in enumerate(node_labels):
1097
+ if all_node_labels.count(node_label) != None: # if in node labels
1098
+ global_node_ix = all_node_labels.index(node_label)
1099
+ node_set = model.rootAssembly.Set(name='node_%i' % (node_label), nodes=instance.nodes[global_node_ix:global_node_ix+1])
1100
+ nodeloads = loads[node_ix*6:node_ix*6+6]
1101
+
1102
+ if not np.all(nodeloads[0:3]==0):
1103
+ model.ConcentratedForce(cf1=nodeloads[0], cf2=nodeloads[1], cf3=nodeloads[2], createStepName=step_name, distributionType=UNIFORM, field='', localCsys=None, name='%sforces_node_%i' % (prefix, node_label), region=node_set)
1104
+
1105
+ if not np.all(nodeloads[3:6]==0):
1106
+ model.Moment(cm1=nodeloads[3], cm2=nodeloads[4], cm3=nodeloads[5], createStepName=step_name, distributionType=UNIFORM, field='', localCsys=None, name='%smoments_node_%i' % (prefix, node_label), region=node_set)
1107
+
1108
+ else:
1109
+ raise ValueError('Node %i does not exist in selected instance.' % (node_label))
1110
+
1111
+
1112
+ def assign_modal_constraint_equation(model, instance_name, name, node_labels, displacement):
1113
+ ndof = 6 # assumes 6 DOFs for all nodes - be aware!
1114
+ instance = model.rootAssembly.instances[instance_name]
1115
+ all_node_labels = [node.label for node in instance.nodes]
1116
+ terms = []
1117
+ for node_ix, node_label in enumerate(node_labels):
1118
+ if all_node_labels.count(node_label) != None: # if in node labels
1119
+ global_node_ix = all_node_labels.index(node_label)
1120
+ node_set_name = 'node_%i' % (node_label)
1121
+ node_set = model.rootAssembly.Set(name=node_set_name, nodes=instance.nodes[global_node_ix:global_node_ix+1])
1122
+ displacement_of_node = displacement[node_ix*ndof:node_ix*ndof+ndof]
1123
+ non_zero = np.where(displacement_of_node !=0 )[0]
1124
+ terms.append([(displacement_of_node[ldof], node_set_name, ldof+1) for ldof in non_zero])
1125
+
1126
+ terms = tuple([term for sublist in terms for term in sublist])
1127
+ model.Equation(name=name, terms=terms)
1128
+