midas-civil 1.0.4__py3-none-any.whl → 1.0.5__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.

Potentially problematic release.


This version of midas-civil might be problematic. Click here for more details.

midas_civil/__init__.py CHANGED
@@ -1,22 +1,24 @@
1
1
  import requests
2
- _version_ = "1.0.4"
2
+ from ._mapi import *
3
+ _version_ = "1.0.5"
3
4
 
4
5
 
5
6
  print('')
6
7
  print('*'*20,' MIDAS CIVIL-NX PYTHON LIBRARY v',_version_,' 🐍 ','*'*20)
7
8
  print('')
8
9
 
9
- resp = requests.get("https://pypi.org/pypi/midas_civil/json").json()
10
- latest_ver = resp["info"]["version"]
11
- if _version_ != latest_ver:
12
- print(
13
- f"⚠️ Warning: You are using v{_version_}, "
14
- f"but the latest available version is v{latest_ver}.\n"
15
- f" Run 'pip install midas_civil --upgrade' to update."
16
- )
17
- print("-"*85)
10
+ if NX.version_check:
11
+ resp = requests.get("https://pypi.org/pypi/midas_civil/json").json()
12
+ latest_ver = resp["info"]["version"]
13
+ if _version_ != latest_ver:
14
+ print(
15
+ f"⚠️ Warning: You are using v{_version_}, "
16
+ f"but the latest available version is v{latest_ver}.\n"
17
+ f" Run 'pip install midas_civil --upgrade' to update."
18
+ )
19
+ print("-"*85)
20
+
18
21
 
19
- from ._mapi import *
20
22
  from ._model import *
21
23
  from ._boundary import *
22
24
  from ._utils import *
@@ -28,6 +30,8 @@ from ._result import *
28
30
 
29
31
  #--- TESTING IMPORTS ---
30
32
  from ._material import *
33
+
34
+ # from ._section import *
31
35
  from ._section import *
32
36
 
33
37
  from ._result_extract import *
@@ -40,4 +44,6 @@ from ._view import *
40
44
 
41
45
  from ._movingload import*
42
46
  from ._settlement import*
47
+ from ._analysiscontrol import*
48
+
43
49
 
@@ -0,0 +1,585 @@
1
+ from ._mapi import *
2
+ #--------------------------------------------------------------------------------------------------
3
+
4
+ class AnalysisControl:
5
+
6
+ class MainControlData:
7
+
8
+ data = []
9
+
10
+ def __init__(self,
11
+ ardc: bool = True,
12
+ anrc: bool = True,
13
+ iter: int = 20,
14
+ tol: float = 0.001,
15
+ csecf: bool = False,
16
+ trs: bool = True,
17
+ crbar: bool = False,
18
+ bmstress: bool = False,
19
+ clats: bool = False):
20
+ """
21
+ Main Control Data constructor for analysis control settings.
22
+
23
+ Parameters:
24
+ ardc: Auto Rotational DOF Constraint for Truss/Plane Stress/Solid Elements (default True)
25
+
26
+ anrc: Auto Normal Rotation Constraint for Plate Elements (default True)
27
+
28
+ iter: Number of Iterations/Load Case (default 20)
29
+
30
+ tol: Convergence Tolerance (default 0.001)
31
+
32
+ csecf: Consider Section Stiffness Scale Factor for Stress Calculation (default False)
33
+
34
+ trs: Transfer Reactions of Slave Node to the Master Node (default True)
35
+
36
+ crbar: Consider Reinforcement for Section Stiffness Calculation (default False)
37
+
38
+ bmstress: Calculate Equivalent Beam Stresses (Von-Mises and Max-Shear) (default False)
39
+
40
+ clats: Change Local Axis of Tapered Section for Force/Stress Calculation (default False)
41
+
42
+ Examples:
43
+ # Basic control data with required parameters
44
+ MainControlData(iter=20, tol=0.001)
45
+
46
+ # with multiple options enabled
47
+ MainControlData(
48
+ ardc=True, anrc=True, iter=30, tol=0.0005,
49
+ trs=True, bmstress=True
50
+ )
51
+ """
52
+
53
+ # Validate required parameters
54
+ if iter is None:
55
+ raise ValueError("iter (Number of Iterations) is required")
56
+ if tol is None:
57
+ raise ValueError("tol (Convergence Tolerance) is required")
58
+
59
+ # ID is always 1
60
+ self.ID = 1
61
+
62
+ # Set parameters
63
+ self.ARDC = ardc
64
+ self.ANRC = anrc
65
+ self.ITER = iter
66
+ self.TOL = tol
67
+ self.CSECF = csecf
68
+ self.TRS = trs
69
+ self.CRBAR = crbar
70
+ self.BMSTRESS = bmstress
71
+ self.CLATS = clats
72
+
73
+ # Add to static list
74
+ AnalysisControl.MainControlData.data.append(self)
75
+
76
+ # Automatically execute the data when instance is created
77
+ self._execute()
78
+
79
+ def _execute(self):
80
+ """
81
+ Automatically sends the MainControlData to the analysis system when created
82
+ """
83
+ json_data = {"Assign": {}}
84
+
85
+ control_data = {
86
+ "ARDC": self.ARDC,
87
+ "ANRC": self.ANRC,
88
+ "ITER": self.ITER,
89
+ "TOL": self.TOL,
90
+ "CSECF": self.CSECF,
91
+ "TRS": self.TRS,
92
+ "CRBAR": self.CRBAR,
93
+ "BMSTRESS": self.BMSTRESS,
94
+ "CLATS": self.CLATS
95
+ }
96
+
97
+ json_data["Assign"][str(self.ID)] = control_data
98
+
99
+ MidasAPI("PUT", "/db/actl", json_data)
100
+
101
+ class PDelta:
102
+ """Create P-Delta Analysis Control Object in Python"""
103
+ data = []
104
+
105
+ def __init__(self,
106
+ iter: int = 5,
107
+ tol: float = 0.00001,
108
+ load_case_data: list = None):
109
+ """
110
+ P-Delta Analysis Control constructor for geometric nonlinear analysis settings.
111
+
112
+ Parameters:
113
+ iter: Number of Iterations (default 5)
114
+
115
+ tol: Convergence Tolerance (default 0.00001)
116
+
117
+ load_case_data: Load Cases with Scale Factors (required)
118
+ - List of load cases and their corresponding scale factors for P-Delta analysis
119
+ - Format: [["LC1", factor1], ["LC2", factor2], ...]
120
+
121
+ Example:
122
+
123
+ PDelta(iter=5, load_case_data=[["DL", 1.0]])
124
+
125
+ """
126
+
127
+ # Validate required parameters
128
+ if iter is None:
129
+ raise ValueError("iter (Number of Iterations) is required")
130
+ if load_case_data is None or len(load_case_data) == 0:
131
+ raise ValueError("load_case_data (Load Cases) is required")
132
+
133
+ # Validate load case data format
134
+ for i, case in enumerate(load_case_data):
135
+ if not isinstance(case, list) or len(case) != 2:
136
+ raise ValueError(f"load_case_data[{i}] must be a list with 2 elements [name, factor]")
137
+ if not isinstance(case[0], str):
138
+ raise ValueError(f"load_case_data[{i}][0] (load case name) must be a string")
139
+ if not isinstance(case[1], (int, float)):
140
+ raise ValueError(f"load_case_data[{i}][1] (scale factor) must be a number")
141
+
142
+ # ID is always 1
143
+ self.ID = 1
144
+
145
+ # Set parameters
146
+ self.ITER = iter
147
+ self.TOL = tol
148
+ self.LOAD_CASE_DATA = load_case_data
149
+
150
+ # Add to static list
151
+ AnalysisControl.PDelta.data.append(self)
152
+
153
+ # Automatically execute the data when instance is created
154
+ self._execute()
155
+
156
+ def _execute(self):
157
+ """
158
+ Automatically sends the P-Delta Analysis Control to the analysis system when created
159
+ """
160
+ json_data = {"Assign": {}}
161
+
162
+ # Convert load case data to required format
163
+ pdel_cases = []
164
+ for case_name, factor in self.LOAD_CASE_DATA:
165
+ pdel_cases.append({
166
+ "LCNAME": case_name,
167
+ "FACTOR": factor
168
+ })
169
+
170
+ control_data = {
171
+ "ITER": self.ITER,
172
+ "TOL": self.TOL,
173
+ "PDEL_CASES": pdel_cases
174
+ }
175
+
176
+ json_data["Assign"][str(self.ID)] = control_data
177
+
178
+ MidasAPI("PUT", "/db/pdel", json_data)
179
+
180
+ class Buckling:
181
+ """Create Buckling Analysis Control Object in Python"""
182
+ data = []
183
+
184
+ def __init__(self,
185
+ mode_num: int = None,
186
+ opt_positive: bool = True,
187
+ load_factor_from: float = 0,
188
+ load_factor_to: float = 0,
189
+ opt_sturm_seq: bool = False,
190
+ opt_consider_axial_only: bool = False,
191
+ load_case_data: list = None):
192
+ """
193
+ Buckling Analysis Control constructor for eigenvalue buckling analysis settings.
194
+
195
+ Parameters:
196
+ mode_num: Number of Modes (required)
197
+
198
+ opt_positive: Load Factor Range Type (default True)
199
+
200
+ load_factor_from: Search From (default 0)
201
+ - Lower bound for load factor search range
202
+ - Only used when opt_positive is False (Search mode)
203
+
204
+ load_factor_to: Search To (default 0)
205
+ - Upper bound for load factor search range
206
+ - Only used when opt_positive is False (Search mode)
207
+
208
+ opt_sturm_seq: Check Sturm Sequence (default False)
209
+
210
+ opt_consider_axial_only: Frame Geometric Stiffness Option (default False)
211
+
212
+ load_case_data: Load Cases with Scale Factors and Types (required)
213
+ - List of load cases with their scale factors and load types
214
+ - Format: [["LC1", factor1, load_type1], ["LC2", factor2, load_type2], ...]
215
+ - Load case name (string), scale factor (number), load type (integer)
216
+ - Load Type: 0=Variable, 1=Constant
217
+
218
+
219
+ Examples:
220
+ Buckling(
221
+ mode_num=8, opt_positive=False,
222
+ load_factor_from=-2.0, load_factor_to=5.0,
223
+ opt_consider_axial_only=True,
224
+ load_case_data=[["Gravity", 1.0, 1], ["Lateral", 1.0, 0]]
225
+ )
226
+ """
227
+
228
+ # Validate required parameters
229
+ if mode_num is None:
230
+ raise ValueError("mode_num (Number of Modes) is required")
231
+ if load_case_data is None or len(load_case_data) == 0:
232
+ raise ValueError("load_case_data (Load Cases) is required")
233
+
234
+ # Validate load case data format
235
+ for i, case in enumerate(load_case_data):
236
+ if not isinstance(case, list) or len(case) != 3:
237
+ raise ValueError(f"load_case_data[{i}] must be a list with 3 elements [name, factor, load_type]")
238
+ if not isinstance(case[0], str):
239
+ raise ValueError(f"load_case_data[{i}][0] (load case name) must be a string")
240
+ if not isinstance(case[1], (int, float)):
241
+ raise ValueError(f"load_case_data[{i}][1] (scale factor) must be a number")
242
+ if not isinstance(case[2], int) or case[2] not in [0, 1]:
243
+ raise ValueError(f"load_case_data[{i}][2] (load type) must be 0 (Variable) or 1 (Constant)")
244
+
245
+ # ID is always 1
246
+ self.ID = 1
247
+
248
+ # Set parameters
249
+ self.MODE_NUM = mode_num
250
+ self.OPT_POSITIVE = opt_positive
251
+ self.LOAD_FACTOR_FROM = load_factor_from
252
+ self.LOAD_FACTOR_TO = load_factor_to
253
+ self.OPT_STURM_SEQ = opt_sturm_seq
254
+ self.OPT_CONSIDER_AXIAL_ONLY = opt_consider_axial_only
255
+ self.LOAD_CASE_DATA = load_case_data
256
+
257
+ # Add to static list
258
+ AnalysisControl.Buckling.data.append(self)
259
+
260
+ # Automatically execute the data when instance is created
261
+ self._execute()
262
+
263
+ def _execute(self):
264
+ """
265
+ Automatically sends the Buckling Analysis Control to the analysis system when created
266
+ """
267
+ json_data = {"Assign": {}}
268
+
269
+ # Convert load case data to required format
270
+ items = []
271
+ for case_name, factor, load_type in self.LOAD_CASE_DATA:
272
+ items.append({
273
+ "LCNAME": case_name,
274
+ "FACTOR": factor,
275
+ "LOAD_TYPE": load_type
276
+ })
277
+
278
+ control_data = {
279
+ "MODE_NUM": self.MODE_NUM,
280
+ "OPT_POSITIVE": self.OPT_POSITIVE,
281
+ "OPT_CONSIDER_AXIAL_ONLY": self.OPT_CONSIDER_AXIAL_ONLY,
282
+ "LOAD_FACTOR_FROM": self.LOAD_FACTOR_FROM,
283
+ "LOAD_FACTOR_TO": self.LOAD_FACTOR_TO,
284
+ "OPT_STURM_SEQ": self.OPT_STURM_SEQ,
285
+ "ITEMS": items
286
+ }
287
+
288
+ json_data["Assign"][str(self.ID)] = control_data
289
+
290
+ MidasAPI("PUT", "/db/buck", json_data)
291
+
292
+
293
+ class EigenValue:
294
+ """Create Eigen Vector Analysis Control Object in Python"""
295
+ data = []
296
+
297
+ def __init__(self,
298
+ analysis_type: str = None,
299
+ # EIGEN specific parameters
300
+ ifreq: int = 1,
301
+ iiter: int = 20,
302
+ idim: int = 1,
303
+ tol: float = 0,
304
+ # LANCZOS specific parameters
305
+ frequency_range: list = None,
306
+ bstrum: bool = False,
307
+ bminmax: bool = None,
308
+ frmin: float = None,
309
+ frmax: float = None,
310
+ # RITZ specific parameters
311
+ bincnl: bool = False,
312
+ ignum: int = None,
313
+ load_vector: list = None,
314
+ vritz: list = None):
315
+ """
316
+ Eigen Vector Analysis Control
317
+
318
+ Parameters:
319
+ analysis_type: Type of Analysis (required)
320
+ - "EIGEN": Subspace Iteration
321
+ - "LANCZOS": Lanczos
322
+ - "RITZ": Ritz Vectors
323
+
324
+ # For EIGEN:
325
+ ifreq: Number of Frequencies (required for EIGEN)
326
+ iiter: Number of Iterations (required for EIGEN)
327
+ idim: Subspace Dimension (default 0, optional for EIGEN)
328
+ tol: Convergence Tolerance (default 0, optional for EIGEN)
329
+
330
+ # For LANCZOS :
331
+ frequency_range: Frequency Range [frmin, frmax] (optional for LANCZOS)
332
+ - If provided, automatically sets bMINMAX=True
333
+ - Format: [min_freq, max_freq]
334
+ bstrum: Sturm Sequence Check (default False, optional for LANCZOS)
335
+
336
+ # For RITZ type only:
337
+ bincnl: Include GL-link Force Vectors (default False, optional for RITZ)
338
+ ignum: Number of Generations for Each GL-link Force (required for RITZ)
339
+ load_vector: Load Cases in simple format (required for RITZ)
340
+ - Format: [["case_or_acc", nog], ...]
341
+ - For ground acceleration: ["ACCX"/"ACCY"/"ACCZ", nog]
342
+ - For load case: ["case_name", nog]
343
+
344
+ Examples:
345
+ # EIGEN analysis
346
+ EigenValue(
347
+ analysis_type="EIGEN",
348
+ ifreq=10,
349
+ iiter=20,
350
+ idim=1,
351
+ tol=1e-10
352
+ )
353
+
354
+ # LANCZOS analysis
355
+ EigenValue(
356
+ analysis_type="LANCZOS",
357
+ ifreq=15,
358
+ frequency_range=[0, 1600], # Automatically sets bMINMAX=True
359
+ bstrum=True
360
+ )
361
+
362
+ # RITZ analysis
363
+ EigenValue(
364
+ analysis_type="RITZ",
365
+ bincnl=False,
366
+ ignum=1,
367
+ load_vector=[["DL", 1], ["ACCX", 1]] # Simple format
368
+ )
369
+ """
370
+
371
+ # Validate required parameters
372
+ if analysis_type is None:
373
+ raise ValueError("analysis_type is required")
374
+ if analysis_type not in ["EIGEN", "LANCZOS", "RITZ"]:
375
+ raise ValueError("analysis_type must be 'EIGEN', 'LANCZOS', or 'RITZ'")
376
+
377
+ # Validate type-specific required parameters
378
+ if analysis_type in ["EIGEN"]:
379
+ if ifreq is None:
380
+ raise ValueError("ifreq (Number of Frequencies) is required for EIGEN")
381
+ if iiter is None:
382
+ raise ValueError("iiter (Number of Iterations) is required for EIGEN")
383
+
384
+ # Handle LANCZOS parameters
385
+ if analysis_type == "LANCZOS":
386
+ # Handle new frequency_range format
387
+ if frequency_range is not None:
388
+ if not isinstance(frequency_range, list) or len(frequency_range) != 2:
389
+ raise ValueError("frequency_range must be a list with exactly 2 elements [frmin, frmax]")
390
+ if frequency_range[0] >= frequency_range[1]:
391
+ raise ValueError("frmin must be less than frmax in frequency_range")
392
+
393
+ # Automatically set parameters
394
+ bminmax = True
395
+ frmin = frequency_range[0]
396
+ frmax = frequency_range[1]
397
+ else:
398
+ # Use legacy parameters or defaults
399
+ if bminmax is None:
400
+ bminmax = False
401
+ if bminmax and (frmin is None or frmax is None):
402
+ raise ValueError("frmin and frmax are required when bminmax is True for LANCZOS")
403
+ if frmin is not None and frmax is not None and frmin >= frmax:
404
+ raise ValueError("frmin must be less than frmax")
405
+
406
+ # Handle RITZ parameters
407
+ if analysis_type == "RITZ":
408
+ if ignum is None:
409
+ raise ValueError("ignum (Number of Generations) is required for RITZ")
410
+
411
+ # Handle new load_vector format
412
+ if load_vector is not None:
413
+ if not isinstance(load_vector, list) or len(load_vector) == 0:
414
+ raise ValueError("load_vector must be a non-empty list")
415
+
416
+ # Convert load_vector to vritz format
417
+ vritz = []
418
+ ground_acc_types = ["ACCX", "ACCY", "ACCZ"]
419
+
420
+ for i, item in enumerate(load_vector):
421
+ if not isinstance(item, list) or len(item) != 2:
422
+ raise ValueError(f"load_vector[{i}] must be a list with exactly 2 elements [name, nog]")
423
+
424
+ name, nog = item
425
+ if not isinstance(name, str):
426
+ raise ValueError(f"load_vector[{i}][0] (name) must be a string")
427
+ if not isinstance(nog, int) or nog <= 0:
428
+ raise ValueError(f"load_vector[{i}][1] (nog) must be a positive integer")
429
+
430
+ # Determine if it's ground acceleration or case
431
+ if name in ground_acc_types:
432
+ vritz.append({
433
+ "KIND": "GROUND",
434
+ "GROUND": name,
435
+ "iNOG": nog
436
+ })
437
+ else:
438
+ vritz.append({
439
+ "KIND": "CASE",
440
+ "CASE": name,
441
+ "iNOG": nog
442
+ })
443
+
444
+ # Use legacy vritz if provided and load_vector is not
445
+ elif vritz is not None:
446
+ if not isinstance(vritz, list) or len(vritz) == 0:
447
+ raise ValueError("vritz (Load Cases) must be a non-empty list")
448
+
449
+ # Validate legacy vritz format
450
+ for i, case in enumerate(vritz):
451
+ if not isinstance(case, dict):
452
+ raise ValueError(f"vritz[{i}] must be a dictionary")
453
+ if "KIND" not in case:
454
+ raise ValueError(f"vritz[{i}] must have 'KIND' key")
455
+ if case["KIND"] not in ["CASE", "GROUND"]:
456
+ raise ValueError(f"vritz[{i}]['KIND'] must be 'CASE' or 'GROUND'")
457
+
458
+ if case["KIND"] == "GROUND":
459
+ if "GROUND" not in case or case["GROUND"] not in ["ACCX", "ACCY", "ACCZ"]:
460
+ raise ValueError(f"vritz[{i}] with KIND='GROUND' must have GROUND='ACCX'/'ACCY'/'ACCZ'")
461
+ elif case["KIND"] == "CASE":
462
+ if "CASE" not in case:
463
+ raise ValueError(f"vritz[{i}] with KIND='CASE' must have 'CASE' key")
464
+
465
+ if "iNOG" not in case:
466
+ raise ValueError(f"vritz[{i}] must have 'iNOG' key")
467
+ else:
468
+ raise ValueError("Either load_vector or vritz is required for RITZ analysis")
469
+
470
+ # ID is always 1
471
+ self.ID = 1
472
+
473
+ # Set parameters
474
+ self.TYPE = analysis_type
475
+ self.iFREQ = ifreq
476
+ self.iITER = iiter
477
+ self.iDIM = idim
478
+ self.TOL = tol
479
+ self.bMINMAX = bminmax
480
+ self.FRMIN = frmin
481
+ self.FRMAX = frmax
482
+ self.bSTRUM = bstrum
483
+ self.bINCNL = bincnl
484
+ self.iGNUM = ignum
485
+ self.vRITZ = vritz
486
+
487
+ # Add to static list
488
+ AnalysisControl.EigenValue.data.append(self)
489
+
490
+ # Automatically execute the data when instance is created
491
+ self._execute()
492
+
493
+ def _execute(self):
494
+ """
495
+ Automatically sends the Eigen Vector Analysis Control to the analysis system when created
496
+ """
497
+ json_data = {"Assign": {}}
498
+
499
+ control_data = {"TYPE": self.TYPE}
500
+
501
+ if self.TYPE in ["EIGEN", "LANCZOS"]:
502
+ control_data.update({
503
+ "iFREQ": self.iFREQ,
504
+ "iITER": self.iITER,
505
+ "iDIM": self.iDIM,
506
+ "TOL": self.TOL
507
+ })
508
+
509
+ if self.TYPE == "LANCZOS":
510
+ control_data.update({
511
+ "bMINMAX": self.bMINMAX,
512
+ "FRMIN": self.FRMIN,
513
+ "FRMAX": self.FRMAX,
514
+ "bSTRUM": self.bSTRUM
515
+ })
516
+
517
+ elif self.TYPE == "RITZ":
518
+ control_data.update({
519
+ "bINCNL": self.bINCNL,
520
+ "iGNUM": self.iGNUM,
521
+ "vRITZ": self.vRITZ
522
+ })
523
+
524
+ json_data["Assign"][str(self.ID)] = control_data
525
+
526
+ MidasAPI("PUT", "/db/eigv", json_data)
527
+
528
+ class Settlement:
529
+
530
+ data = []
531
+
532
+ def __init__(self,
533
+ concurrent_calc: bool = True,
534
+ concurrent_link: bool = True):
535
+ """
536
+ Settlement Analysis Control constructor for settlement analysis settings.
537
+
538
+ Parameters:
539
+ concurrent_calc: Plate Concurrent Force (default True, optional)
540
+ - Active: true
541
+ - Inactive: false
542
+
543
+ concurrent_link: Elastic / General Links Concurrent Force (default True, optional)
544
+ - Active: true
545
+ - Inactive: false
546
+
547
+ Examples:
548
+ # with both Optional value
549
+ Settlement()
550
+
551
+ #without Optional value
552
+ Settlement(
553
+ concurrent_calc=True,
554
+ concurrent_link=False
555
+ )
556
+
557
+ """
558
+
559
+ # ID is always 1
560
+ self.ID = 1
561
+
562
+ # Set parameters
563
+ self.CONCURRENT_CALC = concurrent_calc
564
+ self.CONCURRENT_LINK = concurrent_link
565
+
566
+ # Add to static list
567
+ AnalysisControl.Settlement.data.append(self)
568
+
569
+ # Automatically execute the data when instance is created
570
+ self._execute()
571
+
572
+ def _execute(self):
573
+ """
574
+ Automatically sends the Settlement Analysis Control to the analysis system when created
575
+ """
576
+ json_data = {"Assign": {}}
577
+
578
+ control_data = {
579
+ "CONCURRENT_CALC": self.CONCURRENT_CALC,
580
+ "CONCURRENT_LINK": self.CONCURRENT_LINK
581
+ }
582
+
583
+ json_data["Assign"][str(self.ID)] = control_data
584
+
585
+ MidasAPI("PUT", "/db/smct", json_data)