+2.5 m crestMyeongji profile, 102.5 m deep, drawn to thickness−100 m base

Model review · BN_Myeongji_RUN_01 · 9 November 2026

Myeongji Liquefaction Model Review

This review covers the 2D OpenSees model of the Myeongji embankment, its five earthquake scenarios, and the scripts that build, launch and plot them. Findings fall into two groups: what must be fixed before any result can be used, and what should be fixed so the results can be trusted.

No scenario has produced output yet. The workflow stops twice before OpenSees runs, and Figures 5–8 are drawn from values typed into a script, not from the model. The fix order below gets one real baseline run first.

The workflow today

Where the workflow stops

Six steps take the model from one Tcl file to the report figures. Three of them break, and the step that should run OpenSees has not been reached.

  1. Base model slopeAnalysis2Deff_BN_Myeongji_FULL.tcl Needs decisions 5789 Sends on: one Tcl model, 658,141 lines
  2. Scenario generator generate_all_scenarios.py Breaks 3461011 Sends on: five scenario folders
  3. Deploy deploy_to_a6000.py Works Sends on: the folders, unzipped on a6000
  4. Launch launch_remote_a6000.py Breaks 2 Sends on: five Python processes that only import a library
  5. OpenSees run on a6000 Not reached 38 Sends on: no output files exist
  6. Figures generate_figures.py, postprocess_scenarios.py Typed-in values 115 Makes: Figures 1–8
Each number links to a finding below. The ✕ marks the missing link: no script reads OpenSees output, so the figures could not reflect a run even if one existed.

Sequence

Fix order

Some fixes change what others mean, so the order matters. Each step ends with something you can check.

  1. 1

    Decide two inputs

    57

    Done when the water table level and the permeability unit are confirmed. Both change every result.

  2. 2

    Repair the scenario inputs

    3461011

    Done when the five folders are regenerated and the new checks pass.

  3. 3

    Make it run

    892

    Done when five dynamic steps are timed and SC-01 runs on its own.

  4. 4

    Check the baseline

    1312

    Done when the method matches the centrifuge test and the run is long enough to settle.

  5. 5

    Rebuild the figures

    115

    Done when every chart reads an output file and every label matches the mesh.

Tidy-up, any time: 141617

Group 1 · 8 findings

Must fix

These stop the model from running, or make a finished run describe something other than its label.

1
Must fix

Figures 5–8 are drawn from typed-in values

scratch/postprocess_scenarios.py · lines 50–64, 109–134, 157–160, 189–193, 222–253

What happens
The pore-pressure, settlement, spreading, spectra and summary charts come from formulas and numbers written into the script. No script reads model output. Nothing on the figures marks them as placeholders.
Evidence
SC-01 and SC-04 run byte-identical input files (item 4), yet the charts give them different results. Figure 5(b) also shows a pore-pressure ratio of 0.48 in the clay, which uses a material whose volume response stays elastic and separate from shear.
Fix
Take Figures 5–8 out of any draft now. After a real run, rebuild them from bh_porePressure.out, surf_disp.out, bh_accel.out and the l5_/l6_ files.
Why it matters
Readers would take these as model results, including the 74.5% improvement credited to densification.
SC-01 baselineSC-04 flood
Peak pore-pressure ratio0.00.51.0SC-01 baseline: 0.920.92SC-01SC-04 flood: 0.960.96SC-04Crest settlement (cm)02550SC-01 baseline: 38.538.5SC-01SC-04 flood: 44.244.2SC-04Toe spreading (cm)03060SC-01 baseline: 46.246.2SC-01SC-04 flood: 54.154.1SC-04
# postprocess_scenarios.py, lines 59 and 62
'SC_01_baseline': 0.92 / (1.0 + np.exp(-2.2 * (t - 4.2))) * ...,
'SC_04_flood_gwt': 0.96 / (1.0 + np.exp(-2.4 * (t - 4.0))),
Values as typed in the script. Both scenarios use the same input files, so a model run would return the same numbers for both.
2
Must fix

The launcher does not start OpenSees

scratch/launch_remote_a6000.py · lines 23–24

What happens
Each of the five processes imports OpenSeesPy, prints one line and exits. The .tcl path and log path are built but never used, and the script still reports All scenarios queued. OpenSeesPy also can't run a .tcl file. The setup scripts in scratch/ never confirm a working Tcl version of OpenSees.
Fix
Install the OpenSees Tcl program on a6000. Start OpenSees.exe slopeAnalysis.tcl in each scenario folder and write its output to simulation.log. Do this after items 3 and 8.
Why it matters
The launch looks successful and produces nothing.
Runs today
$pinfo.FileName  = "python"
$pinfo.Arguments = "-c `"import openseespy.opensees;
                    print('Scenario $sc loaded on a6000')`""
Needs to run (sketch)
Start-Process OpenSees.exe -ArgumentList "slopeAnalysis.tcl" `
  -WorkingDirectory $scPath `
  -RedirectStandardOutput $logPath `
  -RedirectStandardError "$scPath\error.log"
The current command loads a Python library and exits, so no model runs.
3
Must fix

The generated scenario files stop after gravity

scratch/generate_all_scenarios.py · lines 59–71 and 142–145

What happens
When the script swaps in the smaller recorder block, it also deletes setTime 0.0, wipeAnalysis and set recDT from section 12. The first recorder line then reads $recDT, which no longer exists, and Tcl stops with can't read recDT: no such variable.
Hidden trap. If only recDT is restored, the clock stays at 55,000 s after gravity. The motion file covers 0–12.5 s, so the base force is zero for the whole run. The run then finishes cleanly with no earthquake.
Base model fileGravity stageclock → 55,000 ssetTime 0.0clock back to 0 sset recDTrecorder step 0.01 sRecordersoutput files openShaking 0–12.5 sforce appliedScenario files todayGravity stageclock → 55,000 ssetTime 0.0removedset recDTremovedTcl errorcan't read recDTRun stopsno dynamic stageIf only set recDT is put backGravity stageclock → 55,000 ssetTime 0.0removedset recDTrecorder step 0.01 sRecordersoutput files openNo shakingfile ended at 12.5 s
The same model clock in three cases. Gravity runs 10 × 500 s, then 10 × 5,000 s, then 10 × 0.005 s, ending at 55,000.05 s. Only the base file resets the clock before shaking.
Fix
Put the three lines back at the top of dynamic_recorders_block, then regenerate all five folders.
Why it matters
All five runs would fail after the slow gravity stage, or finish with no shaking.
# OPTIMIZED VIRTUAL SENSOR ARRAY RECORDERS (DYNAMIC)
+ setTime 0.0
+ wipeAnalysis
  remove recorders
+ set recDT  [expr 2*$motionDT]
  eval "recorder Node -file bh_disp.out ..."
4
Must fix

The flood scenario is a copy of the baseline

scratch/generate_all_scenarios.py · lines 107–113

What happens
SC-04 has no setting that changes the water. Its model file and its motion file have the same checksums as SC-01's.
Fix
In this model the water table comes from the fix <node> 0 0 1 lines, which hold pore pressure at zero. For SC-04, drop those lines for nodes below the new level. Y = +1.0 m is above the free-field ground (Y = 0), so pick a level the mesh can carry, such as Y = 0.
Why it matters
A real run would give SC-04 exactly the same answers as SC-01.
SC_01_baseline/slopeAnalysis.tcl8e728041de1a0990
31e81cb87184c3c9
=
SC_04_flood_gwt/slopeAnalysis.tcl8e728041de1a0990
31e81cb87184c3c9
SC_01_baseline/velocityHistory.out6ea8abbb31efc8d5
76c5b7986a53ec39
=
SC_04_flood_gwt/velocityHistory.out6ea8abbb31efc8d5
76c5b7986a53ec39
MD5 checksums of the two input files in each folder. Identical checksums mean identical files.
5
Must fix

Water table: labels say 0.0 m, the model uses −3.7 m

slopeAnalysis2Deff_BN_Myeongji_FULL.tcl · lines 118753–127978 · generate_figures.py · lines 120–121

What happens
These 9,226 lines fix pore pressure at zero. Every corner node at or above Y = −3.7 m is fixed, and none below it. So the model's water table sits at −3.7 m and Layers 2 and 3 are dry. The figures and scenario names say GWT 0.0 m.
Fix
Check the site investigation and choose one level. If −3.7 m is right, correct the labels. If 0.0 m is right, remove the fixities for nodes between −3.7 m and 0 m, and keep those at Y ≥ 0.
Why it matters
The gap is about 36 kPa of vertical effective stress in Layer 5 (3.7 m × 9.81 kN/m³). More effective stress resists liquefaction, so the model describes a safer site than the labels claim.
As labelledfigures and scenario namesAs modelledpore-pressure fixities0 m-2 m-4 m-6 m-8 m-10 m-12 m-14 mdry, p = 0GWT 0.0 mGWT −3.7 mL4 sand−3.7 to −7.0 mL5 silty sand, liquefiable−7.0 to −13.5 mL6 Busan claybelow −13.5 mL2 0 to −0.8 m · L3 to −3.7 m≈ +36 kPa σ′v
Free-field soil column, drawn to depth scale. Hatching marks the zone where the model holds pore pressure at zero.
6
Must fix

SC-05 changes stiffness, not liquefaction behaviour

scratch/generate_all_scenarios.py · lines 179–183

What happens
The script replaces only the first line of material 5: density, moduli and friction angle. The contraction, dilation and void ratio on the next lines stay at the OpenSees values suggested for Dr = 40%, though the figures say Dr ≈ 75%. The c1 = 0.02 in the comment is never applied. Material 14, the copy of Layer 5 in the side columns, stays loose.
Fix
Replace the whole definition for materials 5 and 14. Layer 7 already uses the Dr = 75% contraction and dilation set and is a good start. Also confirm the improvement should span the full 477 m width, because material 5 does.
Why it matters
Contraction drives pore-pressure build-up, so the improvement result would mostly reflect stiffer soil.
ParameterLayer 5 nowSC-05 as builtDr 75% suggested
Density ρ (t/m³)1.88582.052.1
Shear modulus G (kPa)42,431120,000130,000
Bulk modulus B (kPa)91,934260,000260,000
Friction angle φ (°)313836.5
contrac10.0670.0670.013
contrac30.230.230.0
dilat10.060.060.3
dilat30.270.270.0
Void ratio e0.730.730.55
Side columns (mat 14)looseloosematch mat 5
Shaded cells did not change. The suggested values come from the OpenSees PressureDependMultiYield02 manual.
7
Must fix

Permeability may be about 10 times too high

slopeAnalysis2Deff_BN_Myeongji_FULL.tcl · lines 305380–305388 and matching lines for each layer

What happens
The figures treat these values as m/s (clay k = 10⁻⁹ m/s), and the file passes them to the element unchanged. The official OpenSees PDMY02 example converts m/s first, dividing by gravity and fluid density.
Fix
Confirm the unit in the OpenSees source for 9_4_QuadUP. If the inputs are m/s, divide every hPerm and vPerm by 9.81. Fluid density is 1.0 here.
Why it matters
Water would drain about 10 times faster than intended. That lowers the pore pressure that drives liquefaction.
kinput = k (m/s) ÷ (g · ρw) = k ÷ 9.81
LayerDirectionIn the fileEnter, if m/s is meant
L5 silty sandhorizontal3.0e-63.06e-7
L5 silty sandvertical1.0e-61.02e-7
L6 Busan clayhorizontal5.0e-95.10e-10
L6 Busan clayvertical1.0e-91.02e-10
Two layers shown. The same conversion applies to all 19 materials.
8
Must fix

The solver setup is likely too slow to finish

slopeAnalysis2Deff_BN_Myeongji_FULL.tcl · lines 658096–658098, and 540772–540774 for gravity

What happens
system ProfileSPD stores the matrix as a band. With algorithm Newton it factors that band on every iteration. Tying all 427 base nodes to node 586 roughly doubles the band width.
Fix
Change system ProfileSPD to system UmfPack in both stages, a sparse solver that keeps the matrix small. Time five dynamic steps before launching all five scenarios.
Why it matters
Five scenarios at once could hold a6000 for months.
530,717equations
≈ 12.8 GBmatrix per run
2,618dynamic steps
6 min1 hour1 day1 week1 month1 yearOne factorisationOne factorisation: 18–90 min18–90 minWhole run, one scenarioWhole run, one scenario: ≈ 100–500 days≈ 100–500 days
Rough estimate from the real mesh, not a timing. Assumes reverse Cuthill–McKee ordering, 2–10 GFLOP/s on one core and 3 Newton iterations per step.

Group 2 · 9 findings

Should fix

The model can run without these, but each can make a result look more reliable than it is.

9
Should fix

A failed run still prints “Finished”

slopeAnalysis2Deff_BN_Myeongji_FULL.tcl · lines 658134–658138

What happens
After the time step is halved twice, nothing checks the last analyze call. A run that fails at 6 s still prints Finished with dynamic analysis.
Fix
Keep the result and stop with an error if it failed.
Why it matters
Partial output could be taken as a complete run.
set ok [analyze $remStep $dT]
if {$ok != 0} {
    puts "FAILED at t = [getTime] s"; exit 1
}
10
Should fix

The generator's safety checks can't trigger

scratch/generate_all_scenarios.py · lines 129–148 and 181–184

What happens
find() + len() is never −1, so a missing end marker passes the check and corrupts the file. .replace() does nothing when the text differs, and the script still prints Applied.
Fix
Check each find() before adding the length, and assert the lines each scenario needs. This check would have caught item 3.
Why it matters
A broken input file looks the same as a good one.
i = tcl.find(END_MARKER)
assert i != -1, "end marker missing"
...
for line in ("setTime 0.0", "set recDT", sc["motion_file"]):
    assert line in sc_tcl, f"{sc['id']}: missing {line}"
11
Should fix

Sensors are not where their labels say

scratch/build_sensor_array.py · lines 16–19, 37–40 · generate_all_scenarios.py · lines 24–38

What happens
  • The toe borehole sits at X = 45 m, but the mesh toe is at X ≈ 34 m.
  • Within |X| < 30 m, only 33 of 236 surface nodes are on the crest. 121 are inside the fill and 82 lie under it.
  • 14 of 109 Layer 5 elements sit in Layers 4 or 6, because only each element's first node is checked.
surface sensor on the ground surfacesurface sensor below the surfacetoe borehole sensorscrest borehole sensors
L3L4L5 liquefiable sandL6 clayembankment fill2.50-5-10-15Y (m)-50-2502550X (m)actual toe X ≈ 34toe sensors X = 45
Real node positions from the model, with height exaggerated about 2×. In this window, 215 of 276 surface sensors sit below the ground surface. The crest is flat to X = ±30 m, and the slope meets the ground at X ≈ ±34 m.
Fix
Move the toe column to X ≈ 34 m, take the highest node in each X bin for the surface, and read each element's material tag from its element line.
Why it matters
Toe, surface and Layer 5 outputs would describe other places.
12
Should fix

“Permanent settlement” is measured 6 seconds after strong shaking

scratch/generate_all_scenarios.py · line 129

What happens
The run ends at 12.5 s. By 6.2 s, 95% of the shaking energy (Arias intensity) has arrived, so only about 6 s remain for pore pressure to drain. Settlement from drainage, especially through the clay, takes much longer.
-0.4-0.20+0.2+0.4024681012time (s)a (g)95% of shaking energy2.9–6.2 srun ends 12.5 s≈ 6.3 s left to drainPGA 0.453 g at 4.23 s
Base input acceleration, from the velocity record (dt = 0.005 s). Hover or tap the chart to read values.
Fix
Raise motionSteps to add quiet time after the record (input is zero after the file ends), or label results at 12.5 s. Do this after item 8, because it lengthens the run.
Why it matters
Final settlement would be understated.
13
Should fix

Nothing checks the model against measured behaviour

03_M1_S1_GM31.xlsx · reference papers/

What happens
The project folder holds a centrifuge test and two LEAP papers. No script uses them. Layers 2–5 all share the same generic Dr = 40% calibration.
Fix
Model the centrifuge test first, and compare pore pressure and acceleration with the records. This checks the method. Busan soils still need their own lab data for calibration.
Why it matters
Even a clean run would have nothing showing it is right.
Available, unusedContents
03_M1_S1_GM31.xlsxCentrifuge test 3, Ticino sand, ground motion 31 at 50 g: accelerometers acc1–acc4, pore pressure ppt1–ppt6, displacement D1–D2
KAIST_LEAP-ASIA-2018KAIST centrifuge tests on dense sand, with measured pore pressure and acceleration
2020_Chapter_10LEAP-2017: numerical simulations compared with centrifuge results
14
Should fix

run_scenario_ops.py is an unused stub with wrong values

scratch/run_scenario_ops.py · lines 33–70

What happens
It stops after defining materials and ignores pga_scale and gwt_y. Its comment says it matches the Tcl, but materials 1 and 6 differ and the PDMY02 arguments are out of order.
Fix
Delete it.
Why it matters
Someone could build on wrong soil values.
15
Should fix

Figure labels don't match the model

scratch/generate_figures.py · lines 120–121, 124, 229, 271, 487

Fix
Correct the five labels, and read crest and toe positions from the mesh instead of typing them.
Why it matters
A reviewer comparing the figures with the model will find these differences.
Figure saysModel has
59,259 nodes235,731 nodes. 59,259 are the pore-pressure corner nodes.
Tied periodic side boundariesEach side column is tied within itself. Left and right are not tied together.
Adaptive CFL sub-steppingThe time step is halved after a failed step.
Crest ±25 m, toe ±45 m (Fig. 4c)Crest ±30 m, toe ≈ ±34 m
GWT 0.0 m−3.7 m (item 5)
16
Should fix

22 one-off setup scripts hide the real workflow

BN_Myeongji_RUN_01/scratch/

What happens
Seven check_, seven test_ and four find_ scripts, plus four download and DLL scripts, come from machine setup. Nothing calls them, and several work only under one Windows user path. fix_opensees_dlls.py ignores its own DLL list and copies every DLL into site-packages.
Fix
Once OpenSees runs, write the working install steps in a README. Then delete these scripts, test.tcl and the generated launch .ps1: about 490 lines.
Why it matters
The six scripts that do the work are hard to find.
6 do the work1 unused stub (item 14)22 one-off
The 29 Python scripts in scratch/, one square each.
17
Should fix

No version history or run guide

project folder · scratch/postprocess_scenarios.py · line 22

What happens
The folder sits inside a home-directory repository with no commits, and none of its files are tracked. Regenerating overwrites scenarios/ with no way back. The run order isn't written down, every script assumes it runs from BN_Myeongji_RUN_01, and one output path points to a single Windows user folder.
Fix
Start a repository in the project, ignore the generated folders, write the run order in a short README, and save figures to output/images.
Why it matters
Nobody can trace which inputs made which result.
# in BN_Myeongji_RUN_01
git init
printf "scenarios/\nscratch/*.zip\n" > .gitignore

# README run order
1  scratch/build_sensor_array.py
2  scratch/generate_all_scenarios.py
3  scratch/deploy_to_a6000.py
4  scratch/launch_remote_a6000.py
5  scratch/generate_figures.py, postprocess_scenarios.py

Verified

What already checks out

These parts were checked against the files and are correct.

Scope

Limits of this review