irie 0.0.55__py3-none-any.whl → 0.0.56__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 +178 -177
- irie/apps/prediction/runners/opensees/metrics.py +37 -34
- irie/apps/prediction/views.py +9 -9
- 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.56.dist-info}/METADATA +3 -1
- {irie-0.0.55.dist-info → irie-0.0.56.dist-info}/RECORD +19 -19
- {irie-0.0.55.dist-info → irie-0.0.56.dist-info}/WHEEL +0 -0
- {irie-0.0.55.dist-info → irie-0.0.56.dist-info}/entry_points.txt +0 -0
- {irie-0.0.55.dist-info → irie-0.0.56.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,14 @@ 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
|
|
16
|
+
from xcsi.csi._frame.section import iter_sections
|
|
17
|
+
from xcsi.job import Job
|
|
18
|
+
from xcsi.metrics import PeakDrift
|
|
19
|
+
except:
|
|
20
|
+
pass
|
|
13
21
|
|
|
14
22
|
from irie.apps.prediction.runners import (Runner, RunID, classproperty)
|
|
15
23
|
|
|
@@ -26,6 +34,37 @@ OPENSEES = [
|
|
|
26
34
|
]
|
|
27
35
|
|
|
28
36
|
|
|
37
|
+
def _create_excitation(model, predictor, inputs, dt):
|
|
38
|
+
import numpy as np
|
|
39
|
+
# rotation = predictor.config["orientation"]
|
|
40
|
+
rotation = np.eye(3)*150
|
|
41
|
+
i = 1
|
|
42
|
+
for sensor in predictor.sensorassignment_set.all():
|
|
43
|
+
if sensor.role == "input":
|
|
44
|
+
for dof in range(3):
|
|
45
|
+
series = sum(ai*dx
|
|
46
|
+
for ai, dx in zip(np.array(inputs[sensor.id]["series"]), rotation[dof]))
|
|
47
|
+
|
|
48
|
+
model.timeSeries("Path", i, dt=dt, values=series.tolist())
|
|
49
|
+
model.pattern("UniformExcitation", i, dof+1, accel=i)
|
|
50
|
+
i += 1
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _analyze_and_render(model, artist, nt, dt):
|
|
54
|
+
import veux, veux.motion
|
|
55
|
+
motion = veux.motion.Motion(artist)
|
|
56
|
+
for i in range(nt):
|
|
57
|
+
if model.analyze(1, dt) != 0:
|
|
58
|
+
return -1
|
|
59
|
+
motion.advance(i*dt)
|
|
60
|
+
motion.draw_sections(position=lambda x: [1000*u for u in model.nodeDisp(x)],
|
|
61
|
+
rotation=model.nodeRotation)
|
|
62
|
+
|
|
63
|
+
motion.add_to(artist.canvas)
|
|
64
|
+
return 0
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
29
68
|
@contextlib.contextmanager
|
|
30
69
|
def new_cd(x):
|
|
31
70
|
d = os.getcwd()
|
|
@@ -199,10 +238,14 @@ class OpenSeesRunner(Runner):
|
|
|
199
238
|
def newPrediction(self, event, output_directory = None):
|
|
200
239
|
"""
|
|
201
240
|
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.
|
|
241
|
+
the output directory will be created automatically.
|
|
204
242
|
"""
|
|
205
|
-
|
|
243
|
+
inputs = {}
|
|
244
|
+
for sensor in self.predictor.sensorassignment_set.all():
|
|
245
|
+
if sensor.role == "input":
|
|
246
|
+
inputs[sensor.id] = {"series": sensor.sensor.acceleration(event)}
|
|
247
|
+
|
|
248
|
+
|
|
206
249
|
if output_directory is not None:
|
|
207
250
|
# this case will eventually be deleted, its just for
|
|
208
251
|
# debugging metric renderers.
|
|
@@ -225,23 +268,30 @@ class OpenSeesRunner(Runner):
|
|
|
225
268
|
run_dir.mkdir(parents=True, exist_ok=False)
|
|
226
269
|
|
|
227
270
|
# Copy files to run directory
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
271
|
+
if False:
|
|
272
|
+
event = event.event_file.path
|
|
273
|
+
shutil.copyfile(event, run_dir/"event.zip")
|
|
274
|
+
|
|
275
|
+
model_file = None
|
|
276
|
+
if hasattr(self, "model_file") and self.model_file is not None:
|
|
277
|
+
shutil.copyfile(self.model_file.resolve(),
|
|
278
|
+
run_dir/self.model_file.name)
|
|
279
|
+
|
|
280
|
+
if self.model_file.suffix == ".zip":
|
|
281
|
+
with zipfile.ZipFile(self.model_file, 'r') as zip_ref:
|
|
282
|
+
zip_ref.extractall(run_dir)
|
|
283
|
+
model_file = (run_dir/"nonlinear.tcl").resolve()
|
|
284
|
+
|
|
285
|
+
elif self.model_file.suffix == ".b2k":
|
|
286
|
+
pass
|
|
287
|
+
|
|
288
|
+
elif self.model_file.suffix == ".tcl":
|
|
289
|
+
model_file = (run_dir/self.model_file.name).resolve()
|
|
241
290
|
|
|
242
291
|
self.runs[run_id] = {
|
|
243
292
|
"run_output_directory": run_dir,
|
|
244
|
-
"event_file_name": Path(event),
|
|
293
|
+
# "event_file_name": Path(event),
|
|
294
|
+
"inputs": inputs,
|
|
245
295
|
"model_file": model_file,
|
|
246
296
|
**self.conf
|
|
247
297
|
}
|
|
@@ -264,56 +314,33 @@ class OpenSeesRunner(Runner):
|
|
|
264
314
|
if run_id not in self.runs:
|
|
265
315
|
self._load_config(run_id)
|
|
266
316
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
317
|
+
if False:
|
|
318
|
+
event_file_path = os.path.relpath(self.runs[run_id]["event_file_name"],
|
|
319
|
+
self.model_file.parents[0])
|
|
320
|
+
|
|
321
|
+
output_directory = os.path.relpath(self.runs[run_id]["run_output_directory"],
|
|
322
|
+
self.model_file.parents[0])
|
|
271
323
|
|
|
272
|
-
|
|
324
|
+
event_file_path = self.runs[run_id]["event_file_name"]
|
|
273
325
|
|
|
274
326
|
# Create model
|
|
275
327
|
import opensees.openseespy as ops
|
|
276
328
|
|
|
277
329
|
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']}")
|
|
282
|
-
|
|
283
|
-
model.eval(f"print -json -file modelDetails.json")
|
|
330
|
+
csi = self._csi
|
|
284
331
|
|
|
285
|
-
|
|
332
|
+
with new_cd(self.runs[run_id]["run_output_directory"]):
|
|
286
333
|
|
|
287
|
-
model.
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
}
|
|
334
|
+
model = ops.Model(ndm=3, ndf=6, echo_file=open("model.tcl", "w"))
|
|
335
|
+
if csi is not None:
|
|
336
|
+
asm = Job(csi).assemble(model=model)
|
|
337
|
+
# model = create_model(csi, model=model)
|
|
338
|
+
sections = collect_outlines(csi, model.frame_tags)
|
|
339
|
+
else:
|
|
340
|
+
asm = None
|
|
341
|
+
sections = None
|
|
342
|
+
model.eval(f"source {self.runs[run_id]['model_file']}")
|
|
297
343
|
|
|
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
344
|
|
|
318
345
|
#
|
|
319
346
|
# Run gravity analysis
|
|
@@ -328,61 +355,60 @@ class OpenSeesRunner(Runner):
|
|
|
328
355
|
system SparseGeneral;
|
|
329
356
|
analysis Static;
|
|
330
357
|
analyze 10;
|
|
331
|
-
# write_displacements "dispsGrav.yaml"
|
|
332
358
|
""")
|
|
333
359
|
|
|
334
360
|
#
|
|
335
361
|
# DAMPING
|
|
336
362
|
#
|
|
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"
|
|
363
|
+
# model.eval(r"""
|
|
364
|
+
# set nmodes 8; # Number of modes to analyze for modal analysis
|
|
365
|
+
|
|
366
|
+
# # set wb [eigen -fullGenLapack $nmodes];
|
|
367
|
+
# # puts "\tFundamental-Period After Gravity Analysis:"
|
|
368
|
+
# # for {set iPd 1} {$iPd <= $nmodes} {incr iPd 1} {
|
|
369
|
+
# # set wwb [lindex $wb $iPd-1];
|
|
370
|
+
# # set Tb [expr 2*$pi/sqrt($wwb)];
|
|
371
|
+
# # puts "\tPeriod$iPd= $Tb"
|
|
372
|
+
# # }
|
|
373
|
+
# # write_modes $output_directory/modesPostG.yaml $nmodes
|
|
374
|
+
# # remove recorders
|
|
375
|
+
|
|
376
|
+
# set nmodes [tcl::mathfunc::max {*}$damping_modes $nmodes]
|
|
377
|
+
# set lambdaN [eigen -fullGenLapack $nmodes];
|
|
378
|
+
|
|
379
|
+
# # set lambdaN [eigen $nmodes];
|
|
380
|
+
# if {$damping_type == "rayleigh"} {
|
|
381
|
+
# set nEigenI [lindex $damping_modes 0]; # first rayleigh damping mode
|
|
382
|
+
# set nEigenJ [lindex $damping_modes 1]; # second rayleigh damping mode
|
|
383
|
+
# set iDamp [lindex $damping_ratios 0]; # first rayleigh damping ratio
|
|
384
|
+
# set jDamp [lindex $damping_ratios 1]; # second rayleigh damping ratio
|
|
385
|
+
# set lambdaI [lindex $lambdaN [expr $nEigenI-1]];
|
|
386
|
+
# set lambdaJ [lindex $lambdaN [expr $nEigenJ-1]];
|
|
387
|
+
# set omegaI [expr $lambdaI**0.5];
|
|
388
|
+
# set omegaJ [expr $lambdaJ**0.5];
|
|
389
|
+
# set TI [expr 2.0*$pi/$omegaI];
|
|
390
|
+
# set TJ [expr 2.0*$pi/$omegaJ];
|
|
391
|
+
# set alpha0 [expr 2.0*($iDamp/$omegaI-$jDamp/$omegaJ)/(1/$omegaI**2-1/$omegaJ**2)];
|
|
392
|
+
# set alpha1 [expr 2.0*$iDamp/$omegaI-$alpha0/$omegaI**2];
|
|
393
|
+
# puts "\tRayleigh damping parameters:"
|
|
394
|
+
# puts "\tmodes: $nEigenI, $nEigenJ ; ratios: $iDamp, $jDamp"
|
|
395
|
+
# puts "\tTI = $TI; TJ = $TJ"
|
|
396
|
+
# puts "\tlambdaI = $lambdaI; lambdaJ = $lambdaJ"
|
|
397
|
+
# puts "\tomegaI = $omegaI; omegaJ = $omegaJ"
|
|
398
|
+
# puts "\talpha0 = $alpha0; alpha1 = $alpha1"
|
|
399
|
+
# rayleigh $alpha0 0.0 0.0 $alpha1;
|
|
400
|
+
|
|
401
|
+
# } elseif {$damping_type == "modal"} {
|
|
402
|
+
# # 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.
|
|
403
|
+
# set nratios [llength $damping_ratios]
|
|
404
|
+
# puts "\tModal damping parameters:"
|
|
405
|
+
# puts "\tratios of $damping_ratios at the first $nratios modes"
|
|
406
|
+
# for {set i 1} {$i <= [expr $nmodes - $nratios]} {incr i} {
|
|
407
|
+
# lappend damping_ratios 0
|
|
408
|
+
# }
|
|
409
|
+
# modalDamping {*}$damping_ratios
|
|
346
410
|
# }
|
|
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
|
-
""")
|
|
411
|
+
# """)
|
|
386
412
|
|
|
387
413
|
|
|
388
414
|
#
|
|
@@ -390,73 +416,60 @@ class OpenSeesRunner(Runner):
|
|
|
390
416
|
#
|
|
391
417
|
|
|
392
418
|
## COLUMN SECTION DEFORMATIONS AT TOP AND BOTTOM FOR STRAIN-BASED DAMAGE STATES
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
419
|
+
if False:
|
|
420
|
+
column_strains = tuple(k["key"] for k in self.runs[run_id]["columns"] if k["strain"])
|
|
421
|
+
if len(column_strains) > 0:
|
|
422
|
+
model.recorder("Element", "section", 1, "deformation", xml="eleDef1.txt", ele=column_strains) # section 1 deformation]
|
|
423
|
+
model.recorder("Element", "section", 4, "deformation", xml="eleDef4.txt", ele=column_strains) # section 4 deformation]
|
|
399
424
|
|
|
400
425
|
#
|
|
401
426
|
# Run dynamic analysis
|
|
402
427
|
#
|
|
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
428
|
|
|
412
429
|
# RESPONSE HISTORY RECORDERS
|
|
430
|
+
if False:
|
|
431
|
+
model.recorder("Node", "accel", xml="model/AA_all.txt", timeSeries=(1, 2), dof=(1, 2))
|
|
432
|
+
model.recorder("Node", "accel", xml="model/RD_all.txt", dof=(1, 2))
|
|
413
433
|
|
|
414
|
-
|
|
415
|
-
|
|
434
|
+
column_nodes = tuple(k["node"] for k in self.runs[run_id]["bents"] if k["record"])
|
|
435
|
+
model.recorder("Node", "accel", file="TopColAccel_X_txt.txt", timeSeries=1 , node=column_nodes, dof=1)
|
|
436
|
+
model.recorder("Node", "accel", file="TopColAccel_Y_txt.txt", timeSeries=2 , node=column_nodes, dof=2)
|
|
437
|
+
model.recorder("Node", "disp", file="TopColDrift_X_txt.txt", node=column_nodes, dof=1)
|
|
438
|
+
model.recorder("Node", "disp", file="TopColDrift_Y_txt.txt", node=column_nodes, dof=2)
|
|
416
439
|
|
|
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)
|
|
440
|
+
metrics = [
|
|
441
|
+
PeakDrift((31, 81))
|
|
442
|
+
]
|
|
422
443
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
444
|
+
for metric in metrics:
|
|
445
|
+
metric.record(asm)
|
|
446
|
+
|
|
447
|
+
nt = 500
|
|
448
|
+
dt = 0.02
|
|
449
|
+
_create_excitation(model, self.predictor, self.runs[run_id]["inputs"], dt)
|
|
450
|
+
|
|
451
|
+
model.eval(f"print -json -file model.json")
|
|
452
|
+
|
|
453
|
+
model.eval(f"""
|
|
454
|
+
wipeAnalysis
|
|
429
455
|
set NewmarkGamma 0.50;
|
|
430
|
-
set NewmarkBeta
|
|
456
|
+
set NewmarkBeta 0.25;
|
|
431
457
|
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} _
|
|
458
|
+
numberer RCM;
|
|
459
|
+
test EnergyIncr 1.0e-6 50 0;
|
|
460
|
+
system Umfpack;
|
|
461
|
+
integrator Newmark $NewmarkGamma $NewmarkBeta;
|
|
462
|
+
algorithm Newton;
|
|
463
|
+
analysis Transient;
|
|
464
|
+
""")
|
|
452
465
|
|
|
453
|
-
|
|
466
|
+
import veux
|
|
467
|
+
artist = veux.create_artist(model, vertical=3, model_config={
|
|
468
|
+
"frame_outlines": sections
|
|
469
|
+
})
|
|
470
|
+
_analyze_and_render(model, artist, nt, dt)
|
|
454
471
|
|
|
455
|
-
|
|
456
|
-
# catch {progress update} _
|
|
457
|
-
# set ok [analyze 1 $DtAnalysis];
|
|
458
|
-
# }
|
|
459
|
-
""")
|
|
472
|
+
artist.save("motion.glb")
|
|
460
473
|
|
|
461
474
|
model.wipe()
|
|
462
475
|
|
|
@@ -477,10 +490,7 @@ class OpenSeesRunner(Runner):
|
|
|
477
490
|
else:
|
|
478
491
|
output_dir = self.out_dir/str(run_id)
|
|
479
492
|
|
|
480
|
-
|
|
481
|
-
# model = json.load(f)
|
|
482
|
-
|
|
483
|
-
model = read_model(output_dir/"modelDetails.json")
|
|
493
|
+
model = read_model(output_dir/"model.json")
|
|
484
494
|
|
|
485
495
|
# if type == "COLUMN_STRAIN_STATES":
|
|
486
496
|
# return _clean_json(column_strain_state_metric(model, output_dir, config))
|
|
@@ -504,11 +514,10 @@ class OpenSeesRunner(Runner):
|
|
|
504
514
|
@property
|
|
505
515
|
def _csi(self):
|
|
506
516
|
if not hasattr(self, "_csi_data") or self._csi_data is None:
|
|
507
|
-
from openbim.csi import load, create_model, collect_outlines
|
|
508
517
|
# 1) Parse the CSI file
|
|
509
518
|
try:
|
|
510
519
|
csi_file = self.predictor.config_file
|
|
511
|
-
self._csi_data =
|
|
520
|
+
self._csi_data = load_csi((str(line.decode()).replace("\r\n","\n") for line in csi_file.readlines()))
|
|
512
521
|
except Exception as e:
|
|
513
522
|
import sys
|
|
514
523
|
print(f"Error loading CSiBridge file: {e}", file=sys.stderr)
|
|
@@ -518,21 +527,13 @@ class OpenSeesRunner(Runner):
|
|
|
518
527
|
|
|
519
528
|
|
|
520
529
|
def structural_section(self, name):
|
|
521
|
-
from openbim.csi._frame.section import create_section
|
|
522
|
-
# from openbim.csi._frame.outlines import section_mesh
|
|
523
530
|
if (s:= create_section(self._csi, name)) is not None:
|
|
524
531
|
return {}, s._create_model(mesh_size=0.1)
|
|
525
532
|
|
|
526
533
|
|
|
527
|
-
def structural_sections(self):
|
|
528
|
-
from openbim.csi._frame.section import iter_sections
|
|
534
|
+
def structural_sections(self):
|
|
529
535
|
yield from iter_sections(self._csi)
|
|
530
|
-
|
|
531
|
-
# yield {
|
|
532
|
-
# "name": name,
|
|
533
|
-
# "type": "Section",
|
|
534
|
-
# "section": name,
|
|
535
|
-
# }
|
|
536
|
+
|
|
536
537
|
|
|
537
538
|
def structural_members(self):
|
|
538
539
|
|