from sfsimodels import models
import sfsimodels as sm
import numpy as np
from collections import OrderedDict
from sfsimodels.functions import clean_float
from sfsimodels.build_model_descriptions import build_parameter_descriptions
from liquepy.element.models import ShearTest
from liquepy.exceptions import deprecation
from liquepy.num.models import PM4Sand as PM4SandBase
[docs]class FlacSoil(sm.Soil):
_tension = 0.0 # default to zero
hyst_str = ""
def __str__(self):
return "Base Soil model, id=%i, phi=%.1f" % (self.id, self.phi)
def __init__(self, pw=9800, liq_mass_density=None, g=9.8):
_gravity = g # m/s2
if liq_mass_density:
_liq_mass_density = liq_mass_density # kg/m3
elif pw is not None and _gravity is not None:
if pw == 9800 and g == 9.8:
_liq_mass_density = 1.0e3
else:
_liq_mass_density = pw / _gravity
else:
_liq_mass_density = None
# run parent class initialiser function
super(FlacSoil, self).__init__(liq_mass_density=_liq_mass_density, g=_gravity)
self._extra_class_inputs = ["tension"]
self.inputs = self.inputs + self._extra_class_inputs
self.app2mod = OrderedDict([
("bulk", "bulk_mod"),
("shear", "g_mod"),
("friction", "phi"),
("cohesion", "cohesion"),
("tension", "tension"),
("density", "density"),
("dilation", "dilation_angle"),
("por", "porosity"),
("perm", "flac_permeability")
])
self.required_parameters = []
# for item in self.flac_parameters:
# param = self.flac_parameters[item]
self.required_parameters = list(self.app2mod)
self.optional_parameters = []
if not hasattr(self, "definitions"):
self.definitions = OrderedDict()
self.definitions["density"] = ["Soil mass density", "kg"]
self.definitions["tension"] = ["Soil strength in tension", "Pa"]
self.definitions["flac_permeability"] = ["Permeability of soil", "m^2/Pa.sec"]
self.prop_dict = OrderedDict()
@property
def all_flac_parameters(self):
return self.required_parameters + self.optional_parameters
[docs] def sm_to_fis_name(self, sm_name):
for item in self.app2mod:
if self.app2mod[item] == sm_name:
return item
return None
[docs] def to_fis_mohr_coulomb(self, group_name=None, as_values=False):
mc_params = OrderedDict([
("bulk", "bulk_mod"),
("shear", "g_mod"),
("friction", "phi"),
("cohesion", "cohesion"),
("tension", "tension"),
("density", "density"),
("dilation", "dilation_angle"),
("por", "porosity"),
("perm", "flac_permeability")
])
if group_name is None:
group_name = "'{0}'".format(self.name)
para = ["model mohr notnull group %s" % group_name]
para.append(write_parameters_to_fis_models(self, mc_params, ncols=1, not_null=True, group_name=group_name,
as_values=as_values))
return "\n".join(para)
[docs] def set_prop_dict(self):
plist = []
for item in self.app2mod:
plist.append(self.app2mod[item])
self.prop_dict = build_parameter_descriptions(self, user_p=self.definitions, output="dict", plist=plist)
[docs] def find_units(self, parameter):
if parameter in self.prop_dict:
return self.prop_dict[parameter]["units"]
else:
return None
@property
def density(self):
try:
return self.unit_dry_weight / 9.81
except TypeError:
return None
@property
def tension(self):
return self._tension
@tension.setter
def tension(self, value):
value = clean_float(value)
self._tension = value
@property
def flac_permeability(self):
try:
return self.permeability / self._uww
except TypeError:
return None
[docs]class PM4Sand(FlacSoil, PM4SandBase):
type = "pm4sand"
def __init__(self, pw=9800, liq_mass_density=None, g=9.8, p_atm=101000.0, **kwargs):
# Note: pw has deprecated
_gravity = g # m/s2
if liq_mass_density:
_liq_mass_density = liq_mass_density # kg/m3
elif pw is not None and _gravity is not None:
if pw == 9800 and g == 9.8:
_liq_mass_density = 1.0e3
else:
_liq_mass_density = pw / _gravity
else:
_liq_mass_density = None
FlacSoil.__init__(self, liq_mass_density=_liq_mass_density, g=_gravity)
PM4SandBase.__init__(self, liq_mass_density=_liq_mass_density, g=_gravity, p_atm=p_atm, **kwargs)
self._extra_class_inputs = []
additional_dict = OrderedDict([
("D_r", "relative_density"),
("h_po", "h_po"),
("G_o", "g0_mod"),
("density", "density"),
("porosity", "porosity"),
("h_o", "h_o"),
("e_min", "e_min"),
("e_max", "e_max"),
("n_b", "n_b"),
("n_d", "n_d"),
("c_z", "c_z"),
("c_e", "c_e"),
("n_d", "n_d"),
("k11", "flac_permeability"),
("k22", "flac_permeability"),
("P_atm", "p_atm"),
("phi_cv", "phi_cv"),
("pois", "poissons_ratio"),
("A_do", "a_do"),
("G_degr", "g_degr"),
("Ckaf", "c_kaf"),
("Q_bolt", "q_bolt"),
("R_bolt", "r_bolt"),
("MC_ratio", "mc_ratio"),
("MC_c", "mc_c"),
("z_max", "z_max"),
("c_dr", "c_dr"),
("m_par", "m_par"),
("f_sed", "f_sed"),
("p_sed", "p_sed")
])
self.app2mod.update(additional_dict)
self.pm4sand_parameters = self.app2mod # deprecated
self.required_parameters = ['h_po', 'D_r', 'G_o', 'P_atm', 'density']
self.optional_parameters = [
"k11",
"k22",
"pois",
"h_o",
"n_b",
"n_d",
"A_do",
"z_max",
"c_z",
"c_e",
"phi_cv",
"G_degr",
"c_dr",
"Ckaf",
"Q_bolt",
"R_bolt",
"m_par",
"f_sed",
"p_sed",
"MC_ratio",
"MC_c"
]
def __repr__(self):
return "PM4SandFLAC Soil model, id=%i, phi=%.1f, Dr=%.2f" % (self.id, self.phi, self.relative_density)
def __str__(self):
return "PM4SandFLAC Soil model, id=%i, phi=%.1f, Dr=%.2f" % (self.id, self.phi, self.relative_density)
[docs] def to_fis(self, group_name=None, as_values=False):
params = self.all_flac_parameters
if group_name is None:
group_name = "'{0}'".format(self.name)
para = [f"model pm4sand notnull group {group_name}"]
para.append(write_parameters_to_fis_models(self, params, ncols=1, not_null=True, group_name=group_name,
as_values=as_values))
return para
[docs]def load_element_test(ffp, esig_v0, hydrostatic=0):
ele_data = np.loadtxt(ffp, delimiter=" ", skiprows=1, usecols=(0, 1, 2, 4))
n_count = ele_data[:, 0]
csr_vals = ele_data[:, 1]
tau = csr_vals * esig_v0
strs = ele_data[:, 2] / 100
ru_flac = ele_data[:, 3]
stest = ShearTest(tau, strs, esig_v0=esig_v0, n_cycles=n_count)
stest.set_pp_via_ru(ru_flac, hydrostatic=hydrostatic)
return stest
[docs]def load_file_and_dt(fname):
num_data_k = np.loadtxt(fname, skiprows=4)
time = num_data_k[:, 0] # This get the first column
dt = time[1] - time[0]
values = num_data_k[:, 1]
return values, dt
[docs]def load_file_and_time(fname):
num_data_k = np.loadtxt(fname, skiprows=4)
time = num_data_k[:, 0] # This get the first column
values = num_data_k[:, 1]
return values, time
[docs]def calc_hp0_from_crr_n15_and_relative_density_millen_et_al_2019(crr_n15, d_r):
return crr_n15 * (2.05 - 2.4 * d_r) / (1. - crr_n15 * (12.0 - (12.5 * d_r)))
[docs]def calc_pm4sand_h_po_from_crr_n15_and_relative_density_millen_et_al_2019(crr_n15, d_r):
return crr_n15 * (2.05 - 2.4 * d_r) / (1. - crr_n15 * (12.0 - (12.5 * d_r)))
[docs]def write_parameters_to_fis_models(obj, parameters, ncols=3, not_null=False, group_name=None, as_values=False):
if group_name is None:
group_name = obj.name
count = 0
para = []
pline = ["prop"]
for flac_name in parameters:
if flac_name in obj.app2mod:
ecp_name = obj.app2mod[flac_name]
else:
ecp_name = flac_name
value = getattr(obj, ecp_name)
if value is None:
continue
if as_values:
value = f'{value:.6g}'
else:
value = f'{obj.name}_{ecp_name}'
pline.append(f"{flac_name}={value}")
count += 1
if count == ncols:
if not_null:
pline.append(f"notnull group {group_name}")
else:
pline.append(f"group {group_name}")
para.append(" ".join(pline))
pline = ["prop"]
count = 0
if count:
para.append(" ".join(pline))
return "\n".join(para)
[docs]def write_obj_to_fis_str(obj, parameters, required=''):
para = []
added = []
for item in parameters:
if hasattr(obj, "find_units"):
units = obj.find_units(item)
if units is None or units == "":
units_str = ""
else:
units_str = " ; %s" % obj.find_units(item)
else:
units_str = ""
if item in obj.app2mod:
ecp_name = obj.app2mod[item]
else:
ecp_name = item
val = getattr(obj, ecp_name)
fis_name = f'fis_name = {obj.name}_{ecp_name}'
if fis_name not in added:
added.append(fis_name)
else:
continue
if val is None:
if item in required:
raise sm.ModelError(f"{fis_name} is None")
elif isinstance(val, str):
para.append(f" ={val}{units_str}")
else:
para.append(f" {obj.name}_{ecp_name}={val:.5g}{units_str}")
return para
[docs]def write_soil_profile_obj_to_fis_str(soil_profile, auto_name=True):
"""
This file defines the contents of the soil profile object as parameters in fis language
:param soil_profile:
:return:
"""
num_layers = soil_profile.n_layers
para = ["", "def soil_profile_parameters"]
for i in range(1, num_layers + 1):
sl = soil_profile.layer(i)
if auto_name:
sl.name = "layer_%i" % i
sl.set_prop_dict()
para.append(" ; LAYER %i" % i)
para.append(f'; name: {sl.name}')
para.append(" layer_%i_thickness=%.4g ; m" % (i, soil_profile.layer_height(i)))
para += write_obj_to_fis_str(sl, sl.all_flac_parameters, sl.required_parameters)
para.append(" ;")
para.append(" ; GROUND WATER TABLE (negative)")
para.append(" gwl= -%.4g ; m" % soil_profile.gwl)
para.append(" gravity=%.4g ; m/s2" % 9.8)
para.append(" density_water=%.4g ; kg/m3" % (soil_profile.layer(1)._uww / 9.8))
para.append("end")
para.append("soil_profile_parameters")
return para
[docs]def calc_rayleigh_damping_params(f1, f2, d):
w1 = 2 * np.pi * f1
w2 = 2 * np.pi * f2
beta = 2 * (((w1 * d) - (w2 * d)) / ((w1 ** 2) - (w2 ** 2)))
alpha = beta * w1 * w2
wmin = np.sqrt(alpha / beta)
emin = np.sqrt(alpha * beta)
fmin = wmin / (2 * np.pi)
return emin, fmin
[docs]def check_max_char_limit(fnames, run_loc):
"""
Fis files have a maximum character limit of 200. This function checks that the fis files do not exceed it
:param fnames:
:param run_loc:
:return:
"""
MAX_CHAR_LIMIT = 200 # according to pg 2 of command reference manual this should be 2000
fname_char_limit = 30
for fname in fnames:
if len(fname) > fname_char_limit:
raise ValueError('File name: %s - exceeds limit of %i' % (fname, fname_char_limit))
print(fname)
a = open(run_loc + fname)
lines = a.read().splitlines()
for i, line in enumerate(lines):
if len(line) > MAX_CHAR_LIMIT:
print(line)
raise ValueError('%s - Line %i has %i chars and exceeds limit of %i' % (fname, i + 1, len(line),
MAX_CHAR_LIMIT))
[docs]def pip_freeze_to_run_loc(run_loc):
"""
Saves current list of python packages to the run location
:param run_loc:
:return:
"""
import pip
import datetime
from pip._internal.operations import freeze
reqs = freeze.freeze()
dt = datetime.datetime.today().strftime("%Y%m%d")
ofile = open(run_loc + f'requirements_{dt}.txt', 'w')
ofile.write('\n'.join(reqs))
ofile.close()
# see paper-lateral-spread/nonliq-soil/figure_calibrate_flac_hysteretic.py
[docs]def calc_flac_default_l1_from_ip_millen_2020(i_p):
return 1.7 * i_p ** 0.5 - 3.6
[docs]def calc_flac_default_l2_from_ip_millen_2020(i_p):
return -1.2 * i_p ** -0.25 + 2.25