irie 0.0.55__py3-none-any.whl → 0.0.57__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 irie might be problematic. Click here for more details.
- irie/apps/inventory/models.py +22 -1
- irie/apps/inventory/urls.py +3 -0
- irie/apps/inventory/views.py +67 -2
- irie/apps/prediction/models.py +7 -5
- irie/apps/prediction/runners/opensees/__init__.py +179 -177
- irie/apps/prediction/runners/opensees/metrics.py +37 -34
- irie/apps/prediction/views.py +13 -12
- irie/apps/templates/includes/asset-event-table.html +1 -1
- irie/apps/templates/inventory/asset-profile.html +8 -3
- irie/apps/templates/inventory/asset-sensors.html +33 -12
- irie/apps/templates/inventory/sensor-upload.html +88 -54
- irie/apps/templates/prediction/asset-predictors.html +5 -1
- irie/apps/templates/sensors/render-sensors.js +1 -1
- irie/rest/__main__.py +5 -1
- {irie-0.0.55.dist-info → irie-0.0.57.dist-info}/METADATA +6 -5
- {irie-0.0.55.dist-info → irie-0.0.57.dist-info}/RECORD +19 -19
- {irie-0.0.55.dist-info → irie-0.0.57.dist-info}/WHEEL +0 -0
- {irie-0.0.55.dist-info → irie-0.0.57.dist-info}/entry_points.txt +0 -0
- {irie-0.0.55.dist-info → irie-0.0.57.dist-info}/top_level.txt +0 -0
irie/apps/inventory/models.py
CHANGED
|
@@ -96,6 +96,7 @@ class Asset(models.Model):
|
|
|
96
96
|
ordering = ["-id"]
|
|
97
97
|
|
|
98
98
|
|
|
99
|
+
|
|
99
100
|
class Vulnerability: # (models.Model):
|
|
100
101
|
type = None
|
|
101
102
|
asset = None
|
|
@@ -125,6 +126,7 @@ class SensorGroup(models.Model):
|
|
|
125
126
|
def __str__(self):
|
|
126
127
|
return f"{self.asset.calid} - {self.name} ({self.datum})"
|
|
127
128
|
|
|
129
|
+
|
|
128
130
|
class Sensor(models.Model):
|
|
129
131
|
# class Status:
|
|
130
132
|
# active: bool
|
|
@@ -138,12 +140,31 @@ class Sensor(models.Model):
|
|
|
138
140
|
dy = models.DecimalField(decimal_places=2, max_digits=10)
|
|
139
141
|
dz = models.DecimalField(decimal_places=2, max_digits=10)
|
|
140
142
|
|
|
141
|
-
group = models.ForeignKey(SensorGroup,
|
|
143
|
+
group = models.ForeignKey(SensorGroup,
|
|
144
|
+
related_name="sensors",
|
|
145
|
+
on_delete=models.RESTRICT)
|
|
142
146
|
|
|
143
147
|
def __str__(self):
|
|
144
148
|
return f"{self.group.asset.calid} - {self.name} ({self.group.name})"
|
|
145
149
|
|
|
146
150
|
|
|
151
|
+
def acceleration(self, event):
|
|
152
|
+
import quakeio
|
|
153
|
+
|
|
154
|
+
motion_data = (
|
|
155
|
+
quakeio.read(
|
|
156
|
+
event.event_file.path, input_format="csmip.zip"
|
|
157
|
+
)
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
series = motion_data.match("l", station_channel=f"{self.name}").accel.data
|
|
161
|
+
|
|
162
|
+
return [
|
|
163
|
+
(series*float(self.dx)).tolist(),
|
|
164
|
+
(series*float(self.dy)).tolist(),
|
|
165
|
+
(series*float(self.dz)).tolist()
|
|
166
|
+
]
|
|
167
|
+
|
|
147
168
|
|
|
148
169
|
# class Rendering:
|
|
149
170
|
# def __init__(self, url=None, units, datum):
|
irie/apps/inventory/urls.py
CHANGED
|
@@ -25,8 +25,11 @@ urlpatterns = [
|
|
|
25
25
|
),
|
|
26
26
|
re_path("^inventory/(?P<calid>[0-9 A-Z-]*)/evaluations/$", views.asset_evals, name="asset_evals"),
|
|
27
27
|
re_path("^inventory/(?P<calid>[0-9 A-Z-]*)/$", views.asset_profile, name="asset_profile"),
|
|
28
|
+
# Sensors
|
|
28
29
|
re_path("^inventory/(?P<calid>[0-9 A-Z-]*)/sensors/$", views.asset_sensors, name="asset_sensors"),
|
|
29
30
|
re_path("^inventory/(?P<calid>[0-9 A-Z-]*)/sensor_upload", views.sensor_upload, name="sensor_upload"),
|
|
31
|
+
path("inventory/<slug:calid>/sensors/<int:group_id>/edit/", views.sensor_edit, name="sensor_edit"),
|
|
32
|
+
|
|
30
33
|
|
|
31
34
|
path("inventory/map2/", views.map_inventory),
|
|
32
35
|
path("california.json", views.california_json),
|
irie/apps/inventory/views.py
CHANGED
|
@@ -408,14 +408,16 @@ def asset_sensors(request, calid):
|
|
|
408
408
|
asset = Asset.objects.get(calid=calid)
|
|
409
409
|
context = {
|
|
410
410
|
"asset": asset,
|
|
411
|
+
"segment": "assets",
|
|
411
412
|
"groups": SensorGroup.objects.filter(asset=asset)
|
|
412
413
|
}
|
|
413
414
|
html_template = loader.get_template("inventory/asset-sensors.html")
|
|
414
415
|
return HttpResponse(html_template.render(context, request))
|
|
415
416
|
|
|
417
|
+
|
|
416
418
|
@login_required(login_url="/login/")
|
|
417
419
|
def sensor_upload(request, calid):
|
|
418
|
-
asset = get_object_or_404(Asset, calid=calid)
|
|
420
|
+
asset = get_object_or_404(Asset, calid=calid)
|
|
419
421
|
datums = Datum.objects.filter(asset=asset).values(
|
|
420
422
|
'id', 'orient_x', 'locate_x', 'orient_y', 'locate_y', 'orient_z', 'locate_z'
|
|
421
423
|
)
|
|
@@ -443,7 +445,8 @@ def sensor_upload(request, calid):
|
|
|
443
445
|
formset = SensorFormSet()
|
|
444
446
|
|
|
445
447
|
context = {
|
|
446
|
-
"group_form": group_form,
|
|
448
|
+
"group_form": group_form,
|
|
449
|
+
"segment": "assets",
|
|
447
450
|
"formset": formset,
|
|
448
451
|
"renderings": [
|
|
449
452
|
{"name": predictor.name, "glb": predictor.render_file.url}
|
|
@@ -456,6 +459,68 @@ def sensor_upload(request, calid):
|
|
|
456
459
|
return render(request, "inventory/sensor-upload.html", context)
|
|
457
460
|
|
|
458
461
|
|
|
462
|
+
@login_required(login_url="/login/")
|
|
463
|
+
def sensor_edit(request, calid, group_id):
|
|
464
|
+
"""
|
|
465
|
+
Edit an existing SensorGroup + its Sensors while re-using the
|
|
466
|
+
sensor-upload.html template.
|
|
467
|
+
"""
|
|
468
|
+
from django.forms import modelformset_factory
|
|
469
|
+
|
|
470
|
+
asset = get_object_or_404(Asset, calid=calid)
|
|
471
|
+
sensor_group = get_object_or_404(SensorGroup, pk=group_id, asset=asset)
|
|
472
|
+
|
|
473
|
+
#
|
|
474
|
+
SensorFormSet = modelformset_factory(
|
|
475
|
+
Sensor,
|
|
476
|
+
form=SensorForm,
|
|
477
|
+
extra=0,
|
|
478
|
+
can_delete=True,
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
if request.method == "POST":
|
|
482
|
+
group_form = SensorGroupForm(request.POST, instance=sensor_group, asset=asset)
|
|
483
|
+
formset = SensorFormSet(request.POST, queryset=sensor_group.sensors.all())
|
|
484
|
+
|
|
485
|
+
if group_form.is_valid() and formset.is_valid():
|
|
486
|
+
# update the group fields
|
|
487
|
+
group_form.save()
|
|
488
|
+
|
|
489
|
+
# saves edits, additions (extra rows you may allow), and deletions
|
|
490
|
+
sensors = formset.save(commit=False)
|
|
491
|
+
for sensor in sensors:
|
|
492
|
+
sensor.group = sensor_group
|
|
493
|
+
sensor.save()
|
|
494
|
+
|
|
495
|
+
# any forms flagged for deletion come back in formset.deleted_objects
|
|
496
|
+
for obj in formset.deleted_objects:
|
|
497
|
+
obj.delete()
|
|
498
|
+
|
|
499
|
+
return redirect("asset_sensors", calid=calid)
|
|
500
|
+
else:
|
|
501
|
+
group_form = SensorGroupForm(instance=sensor_group, asset=asset)
|
|
502
|
+
formset = SensorFormSet(queryset=sensor_group.sensors.all())
|
|
503
|
+
|
|
504
|
+
context = {
|
|
505
|
+
"group_form": group_form,
|
|
506
|
+
"segment": "assets",
|
|
507
|
+
"formset": formset,
|
|
508
|
+
"is_edit": True, # optional flag so the template can tweak titles/buttons
|
|
509
|
+
"renderings": [
|
|
510
|
+
{"name": p.name, "glb": p.render_file.url}
|
|
511
|
+
for p in PredictorModel.objects.filter(asset=asset, protocol="IRIE_PREDICTOR_V1")
|
|
512
|
+
if p.render_file and p.render_file.url
|
|
513
|
+
],
|
|
514
|
+
"asset": asset,
|
|
515
|
+
"datums": list(
|
|
516
|
+
Datum.objects.filter(asset=asset).values(
|
|
517
|
+
"id", "orient_x", "locate_x", "orient_y", "locate_y", "orient_z", "locate_z"
|
|
518
|
+
)
|
|
519
|
+
),
|
|
520
|
+
}
|
|
521
|
+
return render(request, "inventory/sensor-upload.html", context)
|
|
522
|
+
|
|
523
|
+
|
|
459
524
|
def _filter_asset_table(request):
|
|
460
525
|
# Copy the GET parameters and remove the 'page' parameter
|
|
461
526
|
page_query = request.GET.copy()
|
irie/apps/prediction/models.py
CHANGED
|
@@ -34,8 +34,10 @@ class PredictorModel(models.Model):
|
|
|
34
34
|
def __str__(self):
|
|
35
35
|
return f"{self.asset.calid} - {self.name} : {self.description}"
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
@property
|
|
38
|
+
def runner(self):
|
|
39
|
+
from irie.apps.prediction.predictor import PREDICTOR_TYPES
|
|
40
|
+
return PREDICTOR_TYPES[self.protocol](self)
|
|
39
41
|
|
|
40
42
|
def get_artist(self):
|
|
41
43
|
pass
|
|
@@ -53,9 +55,9 @@ class SensorAssignment(models.Model):
|
|
|
53
55
|
]
|
|
54
56
|
)
|
|
55
57
|
|
|
56
|
-
orient_z = models.FloatField()
|
|
57
|
-
orient_x = models.FloatField()
|
|
58
|
-
orient_y = models.FloatField()
|
|
58
|
+
# orient_z = models.FloatField()
|
|
59
|
+
# orient_x = models.FloatField()
|
|
60
|
+
# orient_y = models.FloatField()
|
|
59
61
|
# show_x = models.FloatField()
|
|
60
62
|
# show_y = models.FloatField()
|
|
61
63
|
# show_z = models.FloatField()
|
|
@@ -10,6 +10,15 @@ import sys, json
|
|
|
10
10
|
import zipfile
|
|
11
11
|
from pathlib import Path
|
|
12
12
|
import contextlib
|
|
13
|
+
try:
|
|
14
|
+
from xcsi.csi import collect_outlines, load as load_csi
|
|
15
|
+
from xcsi.csi._frame.section import create_section, iter_sections
|
|
16
|
+
from xcsi.job import Job
|
|
17
|
+
from xcsi.metrics import PeakDrift
|
|
18
|
+
except:
|
|
19
|
+
from openbim.csi import load as load_csi, create_model, collect_outlines
|
|
20
|
+
from openbim.csi._frame.section import create_section, iter_sections
|
|
21
|
+
|
|
13
22
|
|
|
14
23
|
from irie.apps.prediction.runners import (Runner, RunID, classproperty)
|
|
15
24
|
|
|
@@ -26,6 +35,37 @@ OPENSEES = [
|
|
|
26
35
|
]
|
|
27
36
|
|
|
28
37
|
|
|
38
|
+
def _create_excitation(model, predictor, inputs, dt):
|
|
39
|
+
import numpy as np
|
|
40
|
+
# rotation = predictor.config["orientation"]
|
|
41
|
+
rotation = np.eye(3)*150
|
|
42
|
+
i = 1
|
|
43
|
+
for sensor in predictor.sensorassignment_set.all():
|
|
44
|
+
if sensor.role == "input":
|
|
45
|
+
for dof in range(3):
|
|
46
|
+
series = sum(ai*dx
|
|
47
|
+
for ai, dx in zip(np.array(inputs[sensor.id]["series"]), rotation[dof]))
|
|
48
|
+
|
|
49
|
+
model.timeSeries("Path", i, dt=dt, values=series.tolist())
|
|
50
|
+
model.pattern("UniformExcitation", i, dof+1, accel=i)
|
|
51
|
+
i += 1
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _analyze_and_render(model, artist, nt, dt):
|
|
55
|
+
import veux, veux.motion
|
|
56
|
+
motion = veux.motion.Motion(artist)
|
|
57
|
+
for i in range(nt):
|
|
58
|
+
if model.analyze(1, dt) != 0:
|
|
59
|
+
return -1
|
|
60
|
+
motion.advance(i*dt)
|
|
61
|
+
motion.draw_sections(position=lambda x: [1000*u for u in model.nodeDisp(x)],
|
|
62
|
+
rotation=model.nodeRotation)
|
|
63
|
+
|
|
64
|
+
motion.add_to(artist.canvas)
|
|
65
|
+
return 0
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
29
69
|
@contextlib.contextmanager
|
|
30
70
|
def new_cd(x):
|
|
31
71
|
d = os.getcwd()
|
|
@@ -199,10 +239,14 @@ class OpenSeesRunner(Runner):
|
|
|
199
239
|
def newPrediction(self, event, output_directory = None):
|
|
200
240
|
"""
|
|
201
241
|
Create a new prediction run and return the run_id. If output_directory is None,
|
|
202
|
-
the output directory will be created automatically.
|
|
203
|
-
will be copied to the new output directory.
|
|
242
|
+
the output directory will be created automatically.
|
|
204
243
|
"""
|
|
205
|
-
|
|
244
|
+
inputs = {}
|
|
245
|
+
for sensor in self.predictor.sensorassignment_set.all():
|
|
246
|
+
if sensor.role == "input":
|
|
247
|
+
inputs[sensor.id] = {"series": sensor.sensor.acceleration(event)}
|
|
248
|
+
|
|
249
|
+
|
|
206
250
|
if output_directory is not None:
|
|
207
251
|
# this case will eventually be deleted, its just for
|
|
208
252
|
# debugging metric renderers.
|
|
@@ -225,23 +269,30 @@ class OpenSeesRunner(Runner):
|
|
|
225
269
|
run_dir.mkdir(parents=True, exist_ok=False)
|
|
226
270
|
|
|
227
271
|
# Copy files to run directory
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
272
|
+
if False:
|
|
273
|
+
event = event.event_file.path
|
|
274
|
+
shutil.copyfile(event, run_dir/"event.zip")
|
|
275
|
+
|
|
276
|
+
model_file = None
|
|
277
|
+
if hasattr(self, "model_file") and self.model_file is not None:
|
|
278
|
+
shutil.copyfile(self.model_file.resolve(),
|
|
279
|
+
run_dir/self.model_file.name)
|
|
280
|
+
|
|
281
|
+
if self.model_file.suffix == ".zip":
|
|
282
|
+
with zipfile.ZipFile(self.model_file, 'r') as zip_ref:
|
|
283
|
+
zip_ref.extractall(run_dir)
|
|
284
|
+
model_file = (run_dir/"nonlinear.tcl").resolve()
|
|
285
|
+
|
|
286
|
+
elif self.model_file.suffix == ".b2k":
|
|
287
|
+
pass
|
|
288
|
+
|
|
289
|
+
elif self.model_file.suffix == ".tcl":
|
|
290
|
+
model_file = (run_dir/self.model_file.name).resolve()
|
|
241
291
|
|
|
242
292
|
self.runs[run_id] = {
|
|
243
293
|
"run_output_directory": run_dir,
|
|
244
|
-
"event_file_name": Path(event),
|
|
294
|
+
# "event_file_name": Path(event),
|
|
295
|
+
"inputs": inputs,
|
|
245
296
|
"model_file": model_file,
|
|
246
297
|
**self.conf
|
|
247
298
|
}
|
|
@@ -264,56 +315,33 @@ class OpenSeesRunner(Runner):
|
|
|
264
315
|
if run_id not in self.runs:
|
|
265
316
|
self._load_config(run_id)
|
|
266
317
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
self.model_file.parents[0])
|
|
318
|
+
if False:
|
|
319
|
+
event_file_path = os.path.relpath(self.runs[run_id]["event_file_name"],
|
|
320
|
+
self.model_file.parents[0])
|
|
271
321
|
|
|
272
|
-
|
|
322
|
+
output_directory = os.path.relpath(self.runs[run_id]["run_output_directory"],
|
|
323
|
+
self.model_file.parents[0])
|
|
324
|
+
|
|
325
|
+
event_file_path = self.runs[run_id]["event_file_name"]
|
|
273
326
|
|
|
274
327
|
# Create model
|
|
275
328
|
import opensees.openseespy as ops
|
|
276
329
|
|
|
277
330
|
import sys
|
|
278
|
-
|
|
279
|
-
model.eval("set argv {}")
|
|
280
|
-
with new_cd(self.runs[run_id]["run_output_directory"]):
|
|
281
|
-
model.eval(f"source {self.runs[run_id]['model_file']}")
|
|
331
|
+
csi = self._csi
|
|
282
332
|
|
|
283
|
-
|
|
333
|
+
with new_cd(self.runs[run_id]["run_output_directory"]):
|
|
284
334
|
|
|
285
|
-
model.
|
|
335
|
+
model = ops.Model(ndm=3, ndf=6, echo_file=open("model.tcl", "w"))
|
|
336
|
+
if csi is not None:
|
|
337
|
+
asm = Job(csi).assemble(model=model)
|
|
338
|
+
# model = create_model(csi, model=model)
|
|
339
|
+
sections = collect_outlines(csi, model.frame_tags)
|
|
340
|
+
else:
|
|
341
|
+
asm = None
|
|
342
|
+
sections = None
|
|
343
|
+
model.eval(f"source {self.runs[run_id]['model_file']}")
|
|
286
344
|
|
|
287
|
-
model.eval(r"""
|
|
288
|
-
proc py {args} {
|
|
289
|
-
global python
|
|
290
|
-
eval "[exec {*}$python {*}$args]"
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
proc pt {args} {
|
|
294
|
-
global python
|
|
295
|
-
puts "[exec {*}$python {*}$args]"
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
proc write_modes {mode_file nmodes} {
|
|
299
|
-
set fid_modes [open $mode_file w+]
|
|
300
|
-
for {set m 1} {$m <= $nmodes} {incr m} {
|
|
301
|
-
puts $fid_modes "$m:"
|
|
302
|
-
foreach n [getNodeTags] {
|
|
303
|
-
puts $fid_modes " $n: \[[join [nodeEigenvector $n $m] {, }]\]";
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
close $fid_modes
|
|
307
|
-
}
|
|
308
|
-
proc write_displacements {file_name {resp Disp}} {
|
|
309
|
-
set fid [open "$file_name" "w+"]
|
|
310
|
-
puts $fid "[getTime]:"
|
|
311
|
-
foreach n [getNodeTags] {
|
|
312
|
-
puts $fid " $n: \[[join [node${resp} $n] {, }]\]";
|
|
313
|
-
}
|
|
314
|
-
close $fid;
|
|
315
|
-
}
|
|
316
|
-
""")
|
|
317
345
|
|
|
318
346
|
#
|
|
319
347
|
# Run gravity analysis
|
|
@@ -328,61 +356,60 @@ class OpenSeesRunner(Runner):
|
|
|
328
356
|
system SparseGeneral;
|
|
329
357
|
analysis Static;
|
|
330
358
|
analyze 10;
|
|
331
|
-
# write_displacements "dispsGrav.yaml"
|
|
332
359
|
""")
|
|
333
360
|
|
|
334
361
|
#
|
|
335
362
|
# DAMPING
|
|
336
363
|
#
|
|
337
|
-
model.eval(r"""
|
|
338
|
-
set nmodes 8; # Number of modes to analyze for modal analysis
|
|
339
|
-
|
|
340
|
-
# set wb [eigen -fullGenLapack $nmodes];
|
|
341
|
-
# puts "\tFundamental-Period After Gravity Analysis:"
|
|
342
|
-
# for {set iPd 1} {$iPd <= $nmodes} {incr iPd 1} {
|
|
343
|
-
# set wwb [lindex $wb $iPd-1];
|
|
344
|
-
# set Tb [expr 2*$pi/sqrt($wwb)];
|
|
345
|
-
# puts "\tPeriod$iPd= $Tb"
|
|
364
|
+
# model.eval(r"""
|
|
365
|
+
# set nmodes 8; # Number of modes to analyze for modal analysis
|
|
366
|
+
|
|
367
|
+
# # set wb [eigen -fullGenLapack $nmodes];
|
|
368
|
+
# # puts "\tFundamental-Period After Gravity Analysis:"
|
|
369
|
+
# # for {set iPd 1} {$iPd <= $nmodes} {incr iPd 1} {
|
|
370
|
+
# # set wwb [lindex $wb $iPd-1];
|
|
371
|
+
# # set Tb [expr 2*$pi/sqrt($wwb)];
|
|
372
|
+
# # puts "\tPeriod$iPd= $Tb"
|
|
373
|
+
# # }
|
|
374
|
+
# # write_modes $output_directory/modesPostG.yaml $nmodes
|
|
375
|
+
# # remove recorders
|
|
376
|
+
|
|
377
|
+
# set nmodes [tcl::mathfunc::max {*}$damping_modes $nmodes]
|
|
378
|
+
# set lambdaN [eigen -fullGenLapack $nmodes];
|
|
379
|
+
|
|
380
|
+
# # set lambdaN [eigen $nmodes];
|
|
381
|
+
# if {$damping_type == "rayleigh"} {
|
|
382
|
+
# set nEigenI [lindex $damping_modes 0]; # first rayleigh damping mode
|
|
383
|
+
# set nEigenJ [lindex $damping_modes 1]; # second rayleigh damping mode
|
|
384
|
+
# set iDamp [lindex $damping_ratios 0]; # first rayleigh damping ratio
|
|
385
|
+
# set jDamp [lindex $damping_ratios 1]; # second rayleigh damping ratio
|
|
386
|
+
# set lambdaI [lindex $lambdaN [expr $nEigenI-1]];
|
|
387
|
+
# set lambdaJ [lindex $lambdaN [expr $nEigenJ-1]];
|
|
388
|
+
# set omegaI [expr $lambdaI**0.5];
|
|
389
|
+
# set omegaJ [expr $lambdaJ**0.5];
|
|
390
|
+
# set TI [expr 2.0*$pi/$omegaI];
|
|
391
|
+
# set TJ [expr 2.0*$pi/$omegaJ];
|
|
392
|
+
# set alpha0 [expr 2.0*($iDamp/$omegaI-$jDamp/$omegaJ)/(1/$omegaI**2-1/$omegaJ**2)];
|
|
393
|
+
# set alpha1 [expr 2.0*$iDamp/$omegaI-$alpha0/$omegaI**2];
|
|
394
|
+
# puts "\tRayleigh damping parameters:"
|
|
395
|
+
# puts "\tmodes: $nEigenI, $nEigenJ ; ratios: $iDamp, $jDamp"
|
|
396
|
+
# puts "\tTI = $TI; TJ = $TJ"
|
|
397
|
+
# puts "\tlambdaI = $lambdaI; lambdaJ = $lambdaJ"
|
|
398
|
+
# puts "\tomegaI = $omegaI; omegaJ = $omegaJ"
|
|
399
|
+
# puts "\talpha0 = $alpha0; alpha1 = $alpha1"
|
|
400
|
+
# rayleigh $alpha0 0.0 0.0 $alpha1;
|
|
401
|
+
|
|
402
|
+
# } elseif {$damping_type == "modal"} {
|
|
403
|
+
# # needs a bit of edit. currently assuming that the ratios are applied in order at the first modes. but should be applied at the specified damping_modes modes.
|
|
404
|
+
# set nratios [llength $damping_ratios]
|
|
405
|
+
# puts "\tModal damping parameters:"
|
|
406
|
+
# puts "\tratios of $damping_ratios at the first $nratios modes"
|
|
407
|
+
# for {set i 1} {$i <= [expr $nmodes - $nratios]} {incr i} {
|
|
408
|
+
# lappend damping_ratios 0
|
|
409
|
+
# }
|
|
410
|
+
# modalDamping {*}$damping_ratios
|
|
346
411
|
# }
|
|
347
|
-
#
|
|
348
|
-
# remove recorders
|
|
349
|
-
|
|
350
|
-
set nmodes [tcl::mathfunc::max {*}$damping_modes $nmodes]
|
|
351
|
-
set lambdaN [eigen -fullGenLapack $nmodes];
|
|
352
|
-
|
|
353
|
-
# set lambdaN [eigen $nmodes];
|
|
354
|
-
if {$damping_type == "rayleigh"} {
|
|
355
|
-
set nEigenI [lindex $damping_modes 0]; # first rayleigh damping mode
|
|
356
|
-
set nEigenJ [lindex $damping_modes 1]; # second rayleigh damping mode
|
|
357
|
-
set iDamp [lindex $damping_ratios 0]; # first rayleigh damping ratio
|
|
358
|
-
set jDamp [lindex $damping_ratios 1]; # second rayleigh damping ratio
|
|
359
|
-
set lambdaI [lindex $lambdaN [expr $nEigenI-1]];
|
|
360
|
-
set lambdaJ [lindex $lambdaN [expr $nEigenJ-1]];
|
|
361
|
-
set omegaI [expr $lambdaI**0.5];
|
|
362
|
-
set omegaJ [expr $lambdaJ**0.5];
|
|
363
|
-
set TI [expr 2.0*$pi/$omegaI];
|
|
364
|
-
set TJ [expr 2.0*$pi/$omegaJ];
|
|
365
|
-
set alpha0 [expr 2.0*($iDamp/$omegaI-$jDamp/$omegaJ)/(1/$omegaI**2-1/$omegaJ**2)];
|
|
366
|
-
set alpha1 [expr 2.0*$iDamp/$omegaI-$alpha0/$omegaI**2];
|
|
367
|
-
puts "\tRayleigh damping parameters:"
|
|
368
|
-
puts "\tmodes: $nEigenI, $nEigenJ ; ratios: $iDamp, $jDamp"
|
|
369
|
-
puts "\tTI = $TI; TJ = $TJ"
|
|
370
|
-
puts "\tlambdaI = $lambdaI; lambdaJ = $lambdaJ"
|
|
371
|
-
puts "\tomegaI = $omegaI; omegaJ = $omegaJ"
|
|
372
|
-
puts "\talpha0 = $alpha0; alpha1 = $alpha1"
|
|
373
|
-
rayleigh $alpha0 0.0 0.0 $alpha1;
|
|
374
|
-
|
|
375
|
-
} elseif {$damping_type == "modal"} {
|
|
376
|
-
# needs a bit of edit. currently assuming that the ratios are applied in order at the first modes. but should be applied at the specified damping_modes modes.
|
|
377
|
-
set nratios [llength $damping_ratios]
|
|
378
|
-
puts "\tModal damping parameters:"
|
|
379
|
-
puts "\tratios of $damping_ratios at the first $nratios modes"
|
|
380
|
-
for {set i 1} {$i <= [expr $nmodes - $nratios]} {incr i} {
|
|
381
|
-
lappend damping_ratios 0
|
|
382
|
-
}
|
|
383
|
-
modalDamping {*}$damping_ratios
|
|
384
|
-
}
|
|
385
|
-
""")
|
|
412
|
+
# """)
|
|
386
413
|
|
|
387
414
|
|
|
388
415
|
#
|
|
@@ -390,73 +417,60 @@ class OpenSeesRunner(Runner):
|
|
|
390
417
|
#
|
|
391
418
|
|
|
392
419
|
## COLUMN SECTION DEFORMATIONS AT TOP AND BOTTOM FOR STRAIN-BASED DAMAGE STATES
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
420
|
+
if False:
|
|
421
|
+
column_strains = tuple(k["key"] for k in self.runs[run_id]["columns"] if k["strain"])
|
|
422
|
+
if len(column_strains) > 0:
|
|
423
|
+
model.recorder("Element", "section", 1, "deformation", xml="eleDef1.txt", ele=column_strains) # section 1 deformation]
|
|
424
|
+
model.recorder("Element", "section", 4, "deformation", xml="eleDef4.txt", ele=column_strains) # section 4 deformation]
|
|
399
425
|
|
|
400
426
|
#
|
|
401
427
|
# Run dynamic analysis
|
|
402
428
|
#
|
|
403
|
-
model.eval(f"""
|
|
404
|
-
wipeAnalysis
|
|
405
|
-
# Uniform Support Excitation
|
|
406
|
-
# lassign [pt -m CE58658.makePattern {event_file_path} --scale $dynamic_scale_factor --node $input_location] dt steps
|
|
407
|
-
# lassign [py -m CE58658.makePattern {event_file_path} --scale $dynamic_scale_factor --node $input_location] dt steps
|
|
408
|
-
set dt 0.1
|
|
409
|
-
set steps 3
|
|
410
|
-
""")
|
|
411
429
|
|
|
412
430
|
# RESPONSE HISTORY RECORDERS
|
|
431
|
+
if False:
|
|
432
|
+
model.recorder("Node", "accel", xml="model/AA_all.txt", timeSeries=(1, 2), dof=(1, 2))
|
|
433
|
+
model.recorder("Node", "accel", xml="model/RD_all.txt", dof=(1, 2))
|
|
413
434
|
|
|
414
|
-
|
|
415
|
-
|
|
435
|
+
column_nodes = tuple(k["node"] for k in self.runs[run_id]["bents"] if k["record"])
|
|
436
|
+
model.recorder("Node", "accel", file="TopColAccel_X_txt.txt", timeSeries=1 , node=column_nodes, dof=1)
|
|
437
|
+
model.recorder("Node", "accel", file="TopColAccel_Y_txt.txt", timeSeries=2 , node=column_nodes, dof=2)
|
|
438
|
+
model.recorder("Node", "disp", file="TopColDrift_X_txt.txt", node=column_nodes, dof=1)
|
|
439
|
+
model.recorder("Node", "disp", file="TopColDrift_Y_txt.txt", node=column_nodes, dof=2)
|
|
416
440
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
model.recorder("Node", "disp", file="TopColDrift_X_txt.txt", node=column_nodes, dof=1)
|
|
421
|
-
model.recorder("Node", "disp", file="TopColDrift_Y_txt.txt", node=column_nodes, dof=2)
|
|
441
|
+
metrics = [
|
|
442
|
+
PeakDrift((31, 81))
|
|
443
|
+
]
|
|
422
444
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
445
|
+
for metric in metrics:
|
|
446
|
+
metric.record(asm)
|
|
447
|
+
|
|
448
|
+
nt = 500
|
|
449
|
+
dt = 0.02
|
|
450
|
+
_create_excitation(model, self.predictor, self.runs[run_id]["inputs"], dt)
|
|
451
|
+
|
|
452
|
+
model.eval(f"print -json -file model.json")
|
|
453
|
+
|
|
454
|
+
model.eval(f"""
|
|
455
|
+
wipeAnalysis
|
|
429
456
|
set NewmarkGamma 0.50;
|
|
430
|
-
set NewmarkBeta
|
|
457
|
+
set NewmarkBeta 0.25;
|
|
431
458
|
constraints Transformation;
|
|
432
|
-
numberer
|
|
433
|
-
test
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
analysis Transient;
|
|
440
|
-
|
|
441
|
-
set DtAnalysis $dt;
|
|
442
|
-
set TmaxAnalysis [expr $dt*$steps];
|
|
443
|
-
set Nsteps $steps;
|
|
444
|
-
if {$dynamic_truncated != 0} {
|
|
445
|
-
set Nsteps $dynamic_timesteps;
|
|
446
|
-
}
|
|
447
|
-
puts "\tGround Motion: dt= $DtAnalysis, NumPts= $Nsteps, TmaxAnalysis= $TmaxAnalysis";
|
|
448
|
-
|
|
449
|
-
puts "\tRunning dynamic ground motion analysis..."
|
|
450
|
-
set t3 [clock clicks -millisec];
|
|
451
|
-
catch {progress create $Nsteps} _
|
|
459
|
+
numberer RCM;
|
|
460
|
+
test EnergyIncr 1.0e-6 50 0;
|
|
461
|
+
system Umfpack;
|
|
462
|
+
integrator Newmark $NewmarkGamma $NewmarkBeta;
|
|
463
|
+
algorithm Newton;
|
|
464
|
+
analysis Transient;
|
|
465
|
+
""")
|
|
452
466
|
|
|
453
|
-
|
|
467
|
+
import veux
|
|
468
|
+
artist = veux.create_artist(model, vertical=3, model_config={
|
|
469
|
+
"frame_outlines": sections
|
|
470
|
+
})
|
|
471
|
+
_analyze_and_render(model, artist, nt, dt)
|
|
454
472
|
|
|
455
|
-
|
|
456
|
-
# catch {progress update} _
|
|
457
|
-
# set ok [analyze 1 $DtAnalysis];
|
|
458
|
-
# }
|
|
459
|
-
""")
|
|
473
|
+
artist.save("motion.glb")
|
|
460
474
|
|
|
461
475
|
model.wipe()
|
|
462
476
|
|
|
@@ -477,10 +491,7 @@ class OpenSeesRunner(Runner):
|
|
|
477
491
|
else:
|
|
478
492
|
output_dir = self.out_dir/str(run_id)
|
|
479
493
|
|
|
480
|
-
|
|
481
|
-
# model = json.load(f)
|
|
482
|
-
|
|
483
|
-
model = read_model(output_dir/"modelDetails.json")
|
|
494
|
+
model = read_model(output_dir/"model.json")
|
|
484
495
|
|
|
485
496
|
# if type == "COLUMN_STRAIN_STATES":
|
|
486
497
|
# return _clean_json(column_strain_state_metric(model, output_dir, config))
|
|
@@ -504,11 +515,10 @@ class OpenSeesRunner(Runner):
|
|
|
504
515
|
@property
|
|
505
516
|
def _csi(self):
|
|
506
517
|
if not hasattr(self, "_csi_data") or self._csi_data is None:
|
|
507
|
-
from openbim.csi import load, create_model, collect_outlines
|
|
508
518
|
# 1) Parse the CSI file
|
|
509
519
|
try:
|
|
510
520
|
csi_file = self.predictor.config_file
|
|
511
|
-
self._csi_data =
|
|
521
|
+
self._csi_data = load_csi((str(line.decode()).replace("\r\n","\n") for line in csi_file.readlines()))
|
|
512
522
|
except Exception as e:
|
|
513
523
|
import sys
|
|
514
524
|
print(f"Error loading CSiBridge file: {e}", file=sys.stderr)
|
|
@@ -518,21 +528,13 @@ class OpenSeesRunner(Runner):
|
|
|
518
528
|
|
|
519
529
|
|
|
520
530
|
def structural_section(self, name):
|
|
521
|
-
from openbim.csi._frame.section import create_section
|
|
522
|
-
# from openbim.csi._frame.outlines import section_mesh
|
|
523
531
|
if (s:= create_section(self._csi, name)) is not None:
|
|
524
532
|
return {}, s._create_model(mesh_size=0.1)
|
|
525
533
|
|
|
526
534
|
|
|
527
|
-
def structural_sections(self):
|
|
528
|
-
from openbim.csi._frame.section import iter_sections
|
|
535
|
+
def structural_sections(self):
|
|
529
536
|
yield from iter_sections(self._csi)
|
|
530
|
-
|
|
531
|
-
# yield {
|
|
532
|
-
# "name": name,
|
|
533
|
-
# "type": "Section",
|
|
534
|
-
# "section": name,
|
|
535
|
-
# }
|
|
537
|
+
|
|
536
538
|
|
|
537
539
|
def structural_members(self):
|
|
538
540
|
|