OSCR

Sparse polynomial surrogates for F-actin networks with compliant crosslinkers.

Code ↔ Paper

3 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 3 matches
  1. [1] § Methods › Homogenization into an affine network › Kinematics and strain-energy functions ↔ generate.py, lines 993–1034 · score 0.62 · Neo Hookean, Mooney Rivlin, oriented, models, network
  2. [2] § Methods › Homogenization into an affine network › Kinematics and strain-energy functions ↔ src/mod_hyperelastic.f90, lines 1–58 · score 0.55 · Mooney Rivlin, Hookean, hyperelastic, Neo, isochoric, Kinematics
  3. [3] § Methods › F-actin with compliant crosslinkers ↔ src/mod_network.f90, lines 1–62 · score 0.50 · Filament force, bending, persistence, Brent, stiffness, contour

Paper

Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

Python · 1,154 lines · 40 KB · no license · 1 match

  1. #!/usr/bin/env python3
  2. """Generate a self-contained material law directory from a JSON configuration.
  3. Usage:
  4. python generate.py config.json # Generate from config
  5. python generate.py --example neo_hooke # Generate example config + material
  6. python generate.py --list # List available model types
  7. """
  8. import argparse
  9. import json
  10. import os
  11. import sys
  12. import stat
  13. from pathlib import Path
  14. SCRIPT_DIR = Path(__file__).resolve().parent
  15. SRC_DIR = SCRIPT_DIR / "src"
  16. # Source files in concatenation order
  17. SOURCE_FILES = [
  18. "mod_constants.f90",
  19. "mod_tensor.f90",
  20. "mod_kinematics.f90",
  21. "mod_continuum.f90",
  22. "mod_hyperelastic.f90",
  23. "mod_icosahedron.f90",
  24. "mod_anisotropic.f90",
  25. "mod_network.f90",
  26. "mod_damage.f90",
  27. "mod_viscosity.f90",
  28. "umat_builder.f90",
  29. "uexternaldb.f90",
  30. ]
  31. # Element-layer sources appended after SOURCE_FILES when emitting uel.f90
  32. UEL_SOURCE_FILES = [
  33. "element/mod_uel_config.f90",
  34. "element/mod_uel_shape.f90",
  35. "element/mod_uel_element.f90",
  36. "element/uel_entry.f90",
  37. ]
  38. # Defaults for the optional "element" config block (UEL emission)
  39. DEFAULT_ELEMENT = {
  40. "type": "u3d8", # 8-node brick (only type currently)
  41. "nint": 8, # volume integration points (1 or 8)
  42. "fbar": True, # F-bar locking treatment (active on the 8-pt brick)
  43. "num_elem": 1, # UEL elements in the real mesh (sizes globalSdv)
  44. "elem_offset": 1000, # dummy-mesh element-number offset (must match deck)
  45. }
  46. # --- Example configurations ---------------------------------------------------
  47. EXAMPLES = {
  48. "neo_hooke": {
  49. "name": "neo_hooke",
  50. "kbulk": 1000.0,
  51. "iso_type": 1, "iso_params": [10.0],
  52. "aniso_type": 0, "n_fiber_fam": 0, "aniso_params": [],
  53. "network_type": 0, "network_params": [],
  54. "damage_type": 0, "damage_params": [],
  55. "n_visco": 0, "visco_params": [],
  56. "test": {"stretch_max": 1.5, "gamma_max": 0.6, "nsteps": 400, "dtime": 0.01},
  57. },
  58. "mooney_rivlin": {
  59. "name": "mooney_rivlin",
  60. "kbulk": 1000.0,
  61. "iso_type": 2, "iso_params": [6.3, 0.012],
  62. "aniso_type": 0, "n_fiber_fam": 0, "aniso_params": [],
  63. "network_type": 0, "network_params": [],
  64. "damage_type": 0, "damage_params": [],
  65. "n_visco": 0, "visco_params": [],
  66. "test": {"stretch_max": 1.5, "gamma_max": 0.6, "nsteps": 400, "dtime": 0.01},
  67. },
  68. "humphrey_hgo": {
  69. "name": "humphrey_hgo",
  70. "kbulk": 500.0,
  71. "iso_type": 4, "iso_params": [2.0, 1.5],
  72. "aniso_type": 1, "n_fiber_fam": 1,
  73. "aniso_params": [100.0, 10.0, 0.226, 1.0, 0.0, 0.0],
  74. "network_type": 0, "network_params": [],
  75. "damage_type": 0, "damage_params": [],
  76. "n_visco": 0, "visco_params": [],
  77. "test": {"stretch_max": 1.3, "gamma_max": 0.4, "nsteps": 400, "dtime": 0.01},
  78. },
  79. "ogden_3term": {
  80. "name": "ogden_3term",
  81. "kbulk": 1000.0,
  82. "iso_type": 3,
  83. "iso_params": [3, 1.3, 5.0, 0.5, -2.0, 0.012, 2.0],
  84. "aniso_type": 0, "n_fiber_fam": 0, "aniso_params": [],
  85. "network_type": 0, "network_params": [],
  86. "damage_type": 0, "damage_params": [],
  87. "n_visco": 0, "visco_params": [],
  88. "test": {"stretch_max": 1.5, "gamma_max": 0.6, "nsteps": 400, "dtime": 0.01},
  89. },
  90. "neo_hooke_damage": {
  91. "name": "neo_hooke_damage",
  92. "kbulk": 1000.0,
  93. "iso_type": 1, "iso_params": [10.0],
  94. "aniso_type": 0, "n_fiber_fam": 0, "aniso_params": [],
  95. "network_type": 0, "network_params": [],
  96. "damage_type": 1, "damage_params": [5.0, 50.0],
  97. "n_visco": 0, "visco_params": [],
  98. "test": {"stretch_max": 1.5, "gamma_max": 0.6, "nsteps": 400, "dtime": 0.01},
  99. },
  100. "neo_hooke_visco": {
  101. "name": "neo_hooke_visco",
  102. "kbulk": 1000.0,
  103. "iso_type": 1, "iso_params": [10.0],
  104. "aniso_type": 0, "n_fiber_fam": 0, "aniso_params": [],
  105. "network_type": 0, "network_params": [],
  106. "damage_type": 0, "damage_params": [],
  107. "n_visco": 1, "visco_params": [0.5, 0.25],
  108. "test": {"stretch_max": 1.5, "gamma_max": 0.6, "nsteps": 400, "dtime": 0.01},
  109. },
  110. "affine_network": {
  111. "name": "affine_network",
  112. "kbulk": 500.0,
  113. "iso_type": 0, "iso_params": [],
  114. "aniso_type": 0, "n_fiber_fam": 0, "aniso_params": [],
  115. "network_type": 5, "network_params": [
  116. 0.5, 1.0e6, 2.0, 1.0, 6, 1.0, 0.0, 0.0,
  117. 1.0, 0.1, 0.01, 2.0, 0.1, 1.0, 0.0, 0.0,
  118. ],
  119. "damage_type": 0, "damage_params": [],
  120. "n_visco": 0, "visco_params": [],
  121. "test": {"stretch_max": 1.3, "gamma_max": 0.3, "nsteps": 200, "dtime": 0.01},
  122. },
  123. "humphrey_fiber": {
  124. "name": "humphrey_fiber",
  125. "kbulk": 500.0,
  126. "iso_type": 4, "iso_params": [2.0, 1.5],
  127. "aniso_type": 2, "n_fiber_fam": 1,
  128. "aniso_params": [100.0, 10.0, 1.0, 0.0, 0.0],
  129. "network_type": 0, "network_params": [],
  130. "damage_type": 0, "damage_params": [],
  131. "n_visco": 0, "visco_params": [],
  132. "test": {"stretch_max": 1.3, "gamma_max": 0.4, "nsteps": 400, "dtime": 0.01},
  133. },
  134. "humphrey_hgo_damage": {
  135. "name": "humphrey_hgo_damage",
  136. "kbulk": 500.0,
  137. "iso_type": 4, "iso_params": [2.0, 1.5],
  138. "aniso_type": 1, "n_fiber_fam": 1,
  139. "aniso_params": [100.0, 10.0, 0.226, 1.0, 0.0, 0.0],
  140. "network_type": 0, "network_params": [],
  141. "damage_type": 1, "damage_params": [5.0, 50.0],
  142. "n_visco": 0, "visco_params": [],
  143. "test": {"stretch_max": 1.3, "gamma_max": 0.4, "nsteps": 400, "dtime": 0.01},
  144. },
  145. "humphrey_fiber_damage": {
  146. "name": "humphrey_fiber_damage",
  147. "kbulk": 500.0,
  148. "iso_type": 4, "iso_params": [2.0, 1.5],
  149. "aniso_type": 2, "n_fiber_fam": 1,
  150. "aniso_params": [100.0, 10.0, 1.0, 0.0, 0.0],
  151. "network_type": 0, "network_params": [],
  152. "damage_type": 1, "damage_params": [5.0, 50.0],
  153. "n_visco": 0, "visco_params": [],
  154. "test": {"stretch_max": 1.3, "gamma_max": 0.4, "nsteps": 400, "dtime": 0.01},
  155. },
  156. "mooney_rivlin_visco": {
  157. "name": "mooney_rivlin_visco",
  158. "kbulk": 1000.0,
  159. "iso_type": 2, "iso_params": [6.3, 0.012],
  160. "aniso_type": 0, "n_fiber_fam": 0, "aniso_params": [],
  161. "network_type": 0, "network_params": [],
  162. "damage_type": 0, "damage_params": [],
  163. "n_visco": 1, "visco_params": [0.5, 0.25],
  164. "test": {"stretch_max": 1.5, "gamma_max": 0.6, "nsteps": 400, "dtime": 0.01},
  165. },
  166. "ogden_visco": {
  167. "name": "ogden_visco",
  168. "kbulk": 1000.0,
  169. "iso_type": 3,
  170. "iso_params": [3, 1.3, 5.0, 0.5, -2.0, 0.012, 2.0],
  171. "aniso_type": 0, "n_fiber_fam": 0, "aniso_params": [],
  172. "network_type": 0, "network_params": [],
  173. "damage_type": 0, "damage_params": [],
  174. "n_visco": 1, "visco_params": [0.5, 0.25],
  175. "test": {"stretch_max": 1.5, "gamma_max": 0.6, "nsteps": 400, "dtime": 0.01},
  176. },
  177. "humphrey_hgo_visco": {
  178. "name": "humphrey_hgo_visco",
  179. "kbulk": 500.0,
  180. "iso_type": 4, "iso_params": [2.0, 1.5],
  181. "aniso_type": 1, "n_fiber_fam": 1,
  182. "aniso_params": [100.0, 10.0, 0.226, 1.0, 0.0, 0.0],
  183. "network_type": 0, "network_params": [],
  184. "damage_type": 0, "damage_params": [],
  185. "n_visco": 1, "visco_params": [0.5, 0.25],
  186. "test": {"stretch_max": 1.3, "gamma_max": 0.4, "nsteps": 400, "dtime": 0.01},
  187. },
  188. "humphrey_fiber_visco": {
  189. "name": "humphrey_fiber_visco",
  190. "kbulk": 500.0,
  191. "iso_type": 4, "iso_params": [2.0, 1.5],
  192. "aniso_type": 2, "n_fiber_fam": 1,
  193. "aniso_params": [100.0, 10.0, 1.0, 0.0, 0.0],
  194. "network_type": 0, "network_params": [],
  195. "damage_type": 0, "damage_params": [],
  196. "n_visco": 1, "visco_params": [0.5, 0.25],
  197. "test": {"stretch_max": 1.3, "gamma_max": 0.4, "nsteps": 400, "dtime": 0.01},
  198. },
  199. "contractile_network": {
  200. "name": "contractile_network",
  201. "kbulk": 500.0,
  202. "iso_type": 0, "iso_params": [],
  203. "aniso_type": 0, "n_fiber_fam": 0, "aniso_params": [],
  204. "network_type": 4, "network_params": [
  205. 0.5, # PHI
  206. 0.2, 2.0, 1.0, # N, B_orient, EFI
  207. 11.0, 11.0, # FRIC, FFMAX
  208. 6, 1.0, 0.0, 0.0, # factor, prefdir
  209. 0.988, 0.804, 38600.0, 0.438, # L, R0F, mu0, beta
  210. 0.065, 1.007, # B0, lambda0
  211. 0.014, 0.667, # R0C, ETAC
  212. 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, # KCH(7) rate constants
  213. ],
  214. "damage_type": 0, "damage_params": [],
  215. "n_visco": 0, "visco_params": [],
  216. "test": {"stretch_max": 1.2, "gamma_max": 0.2, "nsteps": 100, "dtime": 0.01},
  217. },
  218. "mixed_network": {
  219. "name": "mixed_network",
  220. "kbulk": 500.0,
  221. "iso_type": 0, "iso_params": [],
  222. "aniso_type": 0, "n_fiber_fam": 0, "aniso_params": [],
  223. "network_type": 3, "network_params": [
  224. 0.5, # PHI
  225. 5.0e5, 2.0, # N_naff, PP
  226. 5.0e5, 2.0, 1.0, # N_aff, B_orient, EFI
  227. 6, 1.0, 0.0, 0.0, # factor, prefdir
  228. 1.0, 0.1, 0.01, 2.0, 0.1, 1.0, 0.0, 0.0, # filprops(8)
  229. ],
  230. "damage_type": 0, "damage_params": [],
  231. "n_visco": 0, "visco_params": [],
  232. "test": {"stretch_max": 1.3, "gamma_max": 0.3, "nsteps": 200, "dtime": 0.01},
  233. },
  234. "humphrey_hgo_ai": {
  235. "name": "humphrey_hgo_ai",
  236. "kbulk": 500.0,
  237. "iso_type": 4, "iso_params": [2.0, 1.5],
  238. "aniso_type": 3, "n_fiber_fam": 1,
  239. "aniso_params": [100.0, 10.0, 5.0, 6, 1.0, 0.0, 0.0],
  240. "network_type": 0, "network_params": [],
  241. "damage_type": 0, "damage_params": [],
  242. "n_visco": 0, "visco_params": [],
  243. "test": {"stretch_max": 1.3, "gamma_max": 0.4, "nsteps": 200, "dtime": 0.01},
  244. },
  245. "affine_network_linkers": {
  246. "name": "affine_network_linkers",
  247. "kbulk": 500.0,
  248. "iso_type": 0, "iso_params": [],
  249. "aniso_type": 0, "n_fiber_fam": 0, "aniso_params": [],
  250. "network_type": 5, "network_params": [
  251. 0.5, 1.0e6, 2.0, 1.0, 6, 1.0, 0.0, 0.0,
  252. 1.0, 0.1, 0.01, 2.0, 0.1, 1.0, 0.014, 0.6667,
  253. ],
  254. "damage_type": 0, "damage_params": [],
  255. "n_visco": 0, "visco_params": [],
  256. "test": {"stretch_max": 1.3, "gamma_max": 0.3, "nsteps": 200, "dtime": 0.01},
  257. },
  258. "humphrey_muscle": {
  259. "name": "humphrey_muscle",
  260. "kbulk": 500.0,
  261. "iso_type": 4, "iso_params": [2.0, 1.5],
  262. "aniso_type": 5, "n_fiber_fam": 1,
  263. "aniso_params": [100.0, 10.0, 1.0, 0.0, 0.0, 50.0],
  264. "network_type": 0, "network_params": [],
  265. "damage_type": 0, "damage_params": [],
  266. "n_visco": 0, "visco_params": [],
  267. "test": {"stretch_max": 1.3, "gamma_max": 0.4, "nsteps": 400, "dtime": 0.01},
  268. },
  269. "nonaffine_network": {
  270. "name": "nonaffine_network",
  271. "kbulk": 500.0,
  272. "iso_type": 0, "iso_params": [],
  273. "aniso_type": 0, "n_fiber_fam": 0, "aniso_params": [],
  274. "network_type": 6, "network_params": [
  275. 0.5, 1.0e6, 2.0, 6,
  276. 1.0, 0.1, 0.01, 2.0, 0.1, 1.0, 0.0, 0.0,
  277. ],
  278. "damage_type": 0, "damage_params": [],
  279. "n_visco": 0, "visco_params": [],
  280. "test": {"stretch_max": 1.3, "gamma_max": 0.3, "nsteps": 200, "dtime": 0.01},
  281. },
  282. }
  283. # --- Helpers ------------------------------------------------------------------
  284. def compute_nstatev(cfg):
  285. n = 1
  286. if cfg["damage_type"] > 0:
  287. n += 2
  288. n += 9 * cfg["n_visco"]
  289. if cfg["network_type"] == 4: # contractile
  290. # Extract factor from network_params (position 6, 0-indexed)
  291. nparams = cfg["network_params"]
  292. factor = int(nparams[6])
  293. # Icosahedron: 20 faces, each subdivided into factor^2 subtriangles
  294. nwp = 20 * factor * factor
  295. n += 4 + nwp # FRAC(4) + RU0(nwp)
  296. return n
  297. def build_props(cfg):
  298. """Return flat list of all PROPS values."""
  299. p = [
  300. cfg["kbulk"],
  301. float(cfg["iso_type"]),
  302. float(cfg["aniso_type"]),
  303. float(cfg["n_fiber_fam"]),
  304. float(cfg["network_type"]),
  305. float(cfg["damage_type"]),
  306. float(cfg["n_visco"]),
  307. ]
  308. p.extend(cfg["iso_params"])
  309. for _ in range(cfg["n_fiber_fam"]):
  310. p.extend(cfg["aniso_params"])
  311. p.extend(cfg["network_params"])
  312. p.extend(cfg["damage_params"])
  313. p.extend(cfg["visco_params"])
  314. return p
  315. def fmt_props_fortran(props):
  316. """Generate Fortran PROPS assignment lines."""
  317. lines = []
  318. for i, v in enumerate(props, 1):
  319. if v == int(v) and abs(v) < 1e10:
  320. lines.append(f" props({i}) = {v:.1f}d0")
  321. else:
  322. lines.append(f" props({i}) = {v:g}d0")
  323. return "\n".join(lines)
  324. def fmt_props_abaqus(props):
  325. """Format PROPS values for ABAQUS *User Material card (8 per line)."""
  326. lines = []
  327. for i in range(0, len(props), 8):
  328. chunk = props[i : i + 8]
  329. line = ", ".join(f"{v:g}" for v in chunk)
  330. if i + 8 < len(props):
  331. line += ","
  332. lines.append(line)
  333. return "\n".join(lines)
  334. # --- File generators ----------------------------------------------------------
  335. def generate_umat_f90(outdir):
  336. """Concatenate all source modules into a single umat.f90."""
  337. out = outdir / "umat.f90"
  338. with open(out, "w") as f:
  339. f.write("! Auto-generated UMAT — do not edit manually.\n")
  340. f.write("! Regenerate with: python generate.py <config.json>\n\n")
  341. for src in SOURCE_FILES:
  342. path = SRC_DIR / src
  343. f.write(f"! {'='*72}\n")
  344. f.write(f"! SOURCE: {src}\n")
  345. f.write(f"! {'='*72}\n")
  346. f.write(path.read_text())
  347. f.write("\n\n")
  348. def generate_aba_param(outdir):
  349. src = SRC_DIR / "aba_param.inc"
  350. (outdir / "aba_param.inc").write_text(src.read_text())
  351. # --- UEL emission ---------------------------------------------------------
  352. def element_cfg(cfg):
  353. """Merged element block (or None if UEL emission is not requested)."""
  354. if "element" not in cfg:
  355. return None
  356. elem = dict(DEFAULT_ELEMENT)
  357. elem.update(cfg["element"] or {})
  358. return elem
  359. def subst_uel_config(text, elem):
  360. """Rewrite the parameter values in mod_uel_config.f90 from the element block."""
  361. import re
  362. text = re.sub(r"(numElem\s*=\s*)\d+", rf"\g<1>{elem['num_elem']}", text)
  363. text = re.sub(r"(ElemOffset\s*=\s*)\d+", rf"\g<1>{elem['elem_offset']}", text)
  364. fbar = ".true." if elem["fbar"] else ".false."
  365. text = re.sub(r"(use_fbar\s*=\s*)\.\w+\.", rf"\g<1>{fbar}", text)
  366. text = re.sub(r"(nIntPt\s*=\s*)\d+", rf"\g<1>{elem['nint']}", text)
  367. return text
  368. def uel_source(elem):
  369. """Concatenated uel.f90 text: material modules + element layer."""
  370. parts = []
  371. for src in SOURCE_FILES + UEL_SOURCE_FILES:
  372. text = (SRC_DIR / src).read_text()
  373. if src.endswith("mod_uel_config.f90"):
  374. text = subst_uel_config(text, elem)
  375. parts.append(f"! {'='*72}\n! SOURCE: {src}\n! {'='*72}\n" + text)
  376. return ("! Auto-generated UEL (user element + material library) — do not edit.\n"
  377. "! Regenerate with: python generate.py <config.json>\n\n"
  378. + "\n\n".join(parts) + "\n")
  379. def generate_uel_f90(outdir, cfg, elem):
  380. (outdir / "uel.f90").write_text(uel_source(elem))
  381. # Unit-cube nodes in the element's local ordering (sketch in mod_uel_element):
  382. # bottom face z=0: 1(0,0,0) 2(1,0,0) 3(1,1,0) 4(0,1,0); top face z=1: 5..8
  383. UEL_CUBE_COORDS = [
  384. (0.0, 0.0, 0.0), (1.0, 0.0, 0.0), (1.0, 1.0, 0.0), (0.0, 1.0, 0.0),
  385. (0.0, 0.0, 1.0), (1.0, 0.0, 1.0), (1.0, 1.0, 1.0), (0.0, 1.0, 1.0),
  386. ]
  387. def generate_uel_test_driver(outdir, cfg, elem):
  388. """Standalone single-element driver: ramps the unit cube through affine
  389. deformation histories (same load cases as test_umat) and records the
  390. internal force on the x+ face (nodes 2,3,6,7)."""
  391. props = build_props(cfg)
  392. nprops = len(props)
  393. nstatev = compute_nstatev(cfg)
  394. nint = elem["nint"]
  395. test = cfg.get("test", {})
  396. stretch_max = test.get("stretch_max", 1.5)
  397. gamma_max = test.get("gamma_max", 0.6)
  398. nsteps = test.get("nsteps", 400)
  399. dtime = test.get("dtime", 0.01)
  400. coords_lines = "\n".join(
  401. f" coords(1,{i+1}) = {x:.1f}d0; coords(2,{i+1}) = {y:.1f}d0; coords(3,{i+1}) = {z:.1f}d0"
  402. for i, (x, y, z) in enumerate(UEL_CUBE_COORDS))
  403. code = f"""\
  404. ! Auto-generated UEL test driver for material: {cfg["name"]}
  405. ! Drives one U3D8 element through affine deformation ramps (no ABAQUS).
  406. ! Output columns: step time, control parameter, x+ face force (fx, fy, fz).
  407. program test_uel
  408. implicit none
  409. integer, parameter :: nnode = 8, ndofel = 24, mlvarx = 24, nrhs = 1
  410. integer, parameter :: nprops = {nprops}, nsdv = {nstatev}, nintp = {nint}
  411. integer, parameter :: nsvars = nintp*nsdv, njprop = 2
  412. integer, parameter :: mcrd = 3, jtype = 3, jelem = 1
  413. integer, parameter :: ndload = 0, mdload = 1, npredf = 1
  414. double precision :: rhs(mlvarx,1), amatrx(ndofel,ndofel), svars(nsvars)
  415. double precision :: energy(8), props(nprops), coords(mcrd,nnode)
  416. double precision :: u(ndofel), du(mlvarx,1), v(ndofel), a(ndofel)
  417. double precision :: time(2), dtime, params(1)
  418. double precision :: adlmag(mdload,1), ddlmag(mdload,1)
  419. double precision :: predef(2,npredf,nnode), pnewdt, period
  420. integer :: jdltyp(mdload,1), lflags(4), jprops(njprop)
  421. double precision :: Ftar(3,3)
  422. double precision :: stretch_max, gamma_max
  423. integer :: nsteps
  424. double precision, parameter :: zero = 0.0d0, one = 1.0d0
  425. ! --- Element/material setup ---
  426. {fmt_props_fortran(props)}
  427. jprops(1) = nsdv ! local SDVs per integration point
  428. jprops(2) = nsdv ! global SDVs per integration point (UVARM)
  429. lflags = 0
  430. lflags(1) = 1 ! static general step
  431. lflags(2) = 1 ! nlgeom=yes
  432. dtime = {dtime}d0
  433. nsteps = {nsteps}
  434. stretch_max = {stretch_max}d0
  435. gamma_max = {gamma_max}d0
  436. v = zero; a = zero; params = zero; energy = zero
  437. adlmag = zero; ddlmag = zero; predef = zero; jdltyp = 0
  438. period = zero
  439. {coords_lines}
  440. time = zero
  441. call uexternaldb(0, 0, time, zero, 0, 0)
  442. call execute_command_line('mkdir -p results')
  443. ! Uniaxial: F = diag(s, 1/sqrt(s), 1/sqrt(s))
  444. Ftar = zero
  445. Ftar(1,1) = stretch_max
  446. Ftar(2,2) = one/sqrt(stretch_max); Ftar(3,3) = one/sqrt(stretch_max)
  447. call run_case(Ftar, 'results/uel_uniaxial.dat', 'Uniaxial ')
  448. ! Biaxial: F = diag(s, s, 1/s^2)
  449. Ftar = zero
  450. Ftar(1,1) = stretch_max; Ftar(2,2) = stretch_max
  451. Ftar(3,3) = one/(stretch_max*stretch_max)
  452. call run_case(Ftar, 'results/uel_biaxial.dat', 'Biaxial ')
  453. ! Pure shear: F12 = F21 = gamma
  454. Ftar = zero; Ftar(1,1) = one; Ftar(2,2) = one; Ftar(3,3) = one
  455. Ftar(1,2) = gamma_max; Ftar(2,1) = gamma_max
  456. call run_case(Ftar, 'results/uel_shear.dat', 'Shear ')
  457. ! Simple shear: F12 = gamma
  458. Ftar = zero; Ftar(1,1) = one; Ftar(2,2) = one; Ftar(3,3) = one
  459. Ftar(1,2) = gamma_max
  460. call run_case(Ftar, 'results/uel_simple_shear.dat', 'Simple sh')
  461. contains
  462. !> Ramp the element from I to Ftar in nsteps affine increments,
  463. !> carrying svars (state) across increments.
  464. subroutine run_case(Ft, fname, label)
  465. double precision, intent(in) :: Ft(3,3)
  466. character(*), intent(in) :: fname, label
  467. double precision :: F(3,3), uold(ndofel), fface(3), s
  468. integer :: i, n, k, kk, face_nodes(4)
  469. face_nodes = (/2, 3, 6, 7/) ! x+ face (X=1)
  470. svars = zero; u = zero; uold = zero; time = zero
  471. pnewdt = one
  472. open(unit=21, file=fname, status='replace')
  473. do i = 1, nsteps
  474. s = dble(i)/dble(nsteps)
  475. F = identity3() + s*(Ft - identity3())
  476. ! Affine nodal displacements u_a = (F - I) X_a
  477. do n = 1, nnode
  478. do k = 1, 3
  479. u(3*(n-1)+k) = sum((F(k,:) - identity_row(k))*coords(:,n))
  480. end do
  481. end do
  482. du(:,1) = u - uold
  483. rhs = zero; amatrx = zero
  484. call uel(rhs, amatrx, svars, energy, ndofel, nrhs, nsvars, &
  485. props, nprops, coords, mcrd, nnode, u, du, v, a, jtype, &
  486. time, dtime, 1, i, jelem, params, ndload, jdltyp, adlmag, &
  487. predef, npredf, lflags, mlvarx, ddlmag, mdload, pnewdt, &
  488. jprops, njprop, period)
  489. ! Internal force on the x+ face: f = -sum(RHS) over face nodes
  490. fface = zero
  491. do kk = 1, 4
  492. n = face_nodes(kk)
  493. do k = 1, 3
  494. fface(k) = fface(k) - rhs(3*(n-1)+k, 1)
  495. end do
  496. end do
  497. write(21, '(5ES20.10)') time(1), s, fface(1), fface(2), fface(3)
  498. time(1) = time(1) + dtime
  499. uold = u
  500. end do
  501. close(21)
  502. write(*,'(A,A,A)') label, ' -> ', fname
  503. end subroutine run_case
  504. pure function identity3() result(iden)
  505. double precision :: iden(3,3)
  506. integer :: ii
  507. iden = 0.0d0
  508. do ii = 1, 3
  509. iden(ii,ii) = 1.0d0
  510. end do
  511. end function identity3
  512. pure function identity_row(k) result(row)
  513. integer, intent(in) :: k
  514. double precision :: row(3)
  515. row = 0.0d0
  516. row(k) = 1.0d0
  517. end function identity_row
  518. end program test_uel
  519. ! Stub for ABAQUS-provided routine (standalone builds only)
  520. subroutine getoutdir(outdir, lenoutdir)
  521. implicit none
  522. character(len=256), intent(out) :: outdir
  523. integer, intent(out) :: lenoutdir
  524. outdir = '.'
  525. lenoutdir = 1
  526. end subroutine getoutdir
  527. """
  528. (outdir / "test_uel.f90").write_text(code)
  529. def generate_uel_abaqus(outdir, cfg, elem):
  530. """ABAQUS single-element UEL deck: U3 real mesh + dummy mesh for UVARM
  531. visualization. Reuses the bcs_*.inp files written by generate_abaqus_dir
  532. (same node numbering and node sets)."""
  533. abq = outdir / "abaqus"
  534. abq.mkdir(exist_ok=True)
  535. props = build_props(cfg)
  536. nprops = len(props)
  537. nstatev = compute_nstatev(cfg)
  538. nvars = elem["nint"] * nstatev
  539. offset = elem["elem_offset"]
  540. dummy_type = "C3D8" if elem["nint"] == 8 else "C3D8R"
  541. # *UEL PROPERTY data: reals first, the two integer properties last
  542. uel_props = fmt_props_abaqus(list(props) + [nstatev, nstatev])
  543. deck = f"""\
  544. *Heading
  545. UEL single-element test — {cfg["name"]}
  546. ** Real mesh: user element U3 (8-node brick, F-bar={'on' if elem['fbar'] else 'off'}, {elem['nint']}-pt)
  547. ** Dummy mesh: {dummy_type} at element offset {offset}, carries UVARM output
  548. *Node, nset=all_nodes
  549. 1, 1., 1., 1.
  550. 2, 1., 0., 1.
  551. 3, 1., 1., 0.
  552. 4, 1., 0., 0.
  553. 5, 0., 1., 1.
  554. 6, 0., 0., 1.
  555. 7, 0., 1., 0.
  556. 8, 0., 0., 0.
  557. *User Element, type=U3, nodes=8, coordinates=3, properties={nprops}, iproperties=2, variables={nvars}, unsymm
  558. 1,2,3
  559. *Element, type=U3, elset=main_element
  560. 1, 5, 6, 8, 7, 1, 2, 4, 3
  561. *Element, type={dummy_type}, elset=dummy_mesh
  562. {1 + offset}, 5, 6, 8, 7, 1, 2, 4, 3
  563. *Nset, nset=Set-1, generate
  564. 2, 8, 2
  565. *Nset, nset=Set-2, generate
  566. 1, 7, 2
  567. *Nset, nset=Set-3
  568. 1, 2, 5, 6
  569. *Nset, nset=Set-4, generate
  570. 5, 8, 1
  571. *Nset, nset=Set-5
  572. 2, 4, 6, 8
  573. *Nset, nset=Set-6
  574. 3, 4, 7, 8
  575. *Nset, nset=Set-7, generate
  576. 1, 4, 1
  577. *Uel Property, elset=main_element
  578. {uel_props}
  579. *Solid Section, elset=dummy_mesh, material=dummy_material
  580. *Material, name=dummy_material
  581. *User output variables
  582. {nstatev},
  583. *Elastic
  584. 1.e-20
  585. *Step, name=static, nlgeom=YES, unsymm=YES, inc=200
  586. *Static
  587. 0.01, 1., 1e-05, 0.1
  588. *INCLUDE, file=bcs_uni.inp
  589. *OUTPUT,FIELD,VARIABLE=PRESELECT,FREQ=1
  590. *ELEMENT OUTPUT, elset=dummy_mesh
  591. UVARM
  592. *OUTPUT,HISTORY,VARIABLE=PRESELECT,FREQ=1
  593. *End Step
  594. """
  595. (abq / "uel_cube.inp").write_text(deck)
  596. run_sh = """\
  597. #!/bin/bash
  598. # Run ABAQUS single-element UEL test
  599. # Usage: ./run_uel.sh [bcs_file]
  600. BCS=${1:-bcs_uni.inp}
  601. sed -i "s/INCLUDE, file=bcs_.*/INCLUDE, file=${BCS}/" uel_cube.inp
  602. abaqus job=uel_cube user=../uel.f90 interactive
  603. """
  604. run_path = abq / "run_uel.sh"
  605. run_path.write_text(run_sh)
  606. run_path.chmod(run_path.stat().st_mode | stat.S_IEXEC)
  607. def generate_test_driver(outdir, cfg):
  608. props = build_props(cfg)
  609. nprops = len(props)
  610. nstatev = compute_nstatev(cfg)
  611. test = cfg.get("test", {})
  612. stretch_max = test.get("stretch_max", 1.5)
  613. gamma_max = test.get("gamma_max", 0.6)
  614. nsteps = test.get("nsteps", 400)
  615. dtime = test.get("dtime", 0.01)
  616. code = f"""\
  617. ! Auto-generated test driver for material: {cfg["name"]}
  618. ! Runs uniaxial, biaxial, pure shear, and simple shear tests.
  619. program test_umat
  620. implicit none
  621. integer, parameter :: ntens = 6, ndi = 3, nshr = 3
  622. integer, parameter :: nprops = {nprops}, nstatev = {nstatev}
  623. integer, parameter :: noel = 1, npt = 1
  624. double precision :: stress(ntens), statev(nstatev), ddsdde(ntens, ntens)
  625. double precision :: ddsddt(ntens), drplde(ntens)
  626. double precision :: stran(ntens), dstran(ntens)
  627. double precision :: time(2), predef(1), dpred(1)
  628. double precision :: props(nprops), coords(3), drot(3,3)
  629. double precision :: dfgrd0(3,3), dfgrd1(3,3)
  630. double precision :: sse, spd, scd, rpl, drpldt, dtime
  631. double precision :: temp, dtemp, pnewdt, celent
  632. character(len=8) :: cmname
  633. integer :: layer, kspt, kstep, kinc
  634. integer :: nsteps, i
  635. double precision :: stretch, dstretch, gamma_val, dgamma
  636. double precision, parameter :: zero = 0.0d0, one = 1.0d0
  637. ! --- Initialize all arrays ---
  638. stress = zero; statev = zero; ddsdde = zero
  639. stran = zero; dstran = zero; time = zero
  640. drot = zero; dfgrd0 = zero; dfgrd1 = zero
  641. coords = zero; predef = zero; dpred = zero
  642. temp = zero; dtemp = zero; pnewdt = one; celent = one
  643. rpl = zero; drpldt = zero; ddsddt = zero; drplde = zero
  644. layer = 1; kspt = 1; kinc = 1; cmname = 'UMAT'
  645. dfgrd0(1,1) = one; dfgrd0(2,2) = one; dfgrd0(3,3) = one
  646. ! --- Initialize external database (for RW network models) ---
  647. call uexternaldb(0, 0, time, zero, 0, 0)
  648. ! --- Material properties ---
  649. {fmt_props_fortran(props)}
  650. ! --- Test parameters ---
  651. nsteps = {nsteps}
  652. dtime = {dtime}d0
  653. dstretch = ({stretch_max}d0 - one) / nsteps
  654. dgamma = (2.0d0 * {gamma_max}d0) / nsteps
  655. call execute_command_line('mkdir -p results')
  656. ! ========================== UNIAXIAL ==========================
  657. stress = zero; statev = zero; time = zero
  658. dfgrd1 = zero; dfgrd1(1,1) = one; dfgrd1(2,2) = one; dfgrd1(3,3) = one
  659. stretch = one
  660. open(unit=10, file='results/uniaxial.dat', status='replace')
  661. do i = 1, nsteps
  662. dfgrd1(1,1) = stretch
  663. dfgrd1(2,2) = one / sqrt(stretch)
  664. dfgrd1(3,3) = one / sqrt(stretch)
  665. call umat(stress, statev, ddsdde, sse, spd, scd, rpl, ddsddt, &
  666. drplde, drpldt, stran, dstran, time, dtime, temp, dtemp, &
  667. predef, dpred, cmname, ndi, nshr, ntens, nstatev, props, &
  668. nprops, coords, drot, pnewdt, celent, dfgrd0, dfgrd1, &
  669. noel, npt, layer, kspt, kstep, kinc)
  670. write(10, '(3ES20.10)') time(1), stretch, stress(1)
  671. time(1) = time(1) + dtime
  672. stretch = stretch + dstretch
  673. end do
  674. close(10)
  675. write(*, '(A)') 'Uniaxial -> results/uniaxial.dat'
  676. ! ========================== BIAXIAL ==========================
  677. stress = zero; statev = zero; time = zero
  678. dfgrd1 = zero; dfgrd1(1,1) = one; dfgrd1(2,2) = one; dfgrd1(3,3) = one
  679. stretch = one
  680. open(unit=11, file='results/biaxial.dat', status='replace')
  681. do i = 1, nsteps
  682. dfgrd1(1,1) = stretch
  683. dfgrd1(2,2) = stretch
  684. dfgrd1(3,3) = one / (stretch * stretch)
  685. call umat(stress, statev, ddsdde, sse, spd, scd, rpl, ddsddt, &
  686. drplde, drpldt, stran, dstran, time, dtime, temp, dtemp, &
  687. predef, dpred, cmname, ndi, nshr, ntens, nstatev, props, &
  688. nprops, coords, drot, pnewdt, celent, dfgrd0, dfgrd1, &
  689. noel, npt, layer, kspt, kstep, kinc)
  690. write(11, '(3ES20.10)') time(1), stretch, stress(1)
  691. time(1) = time(1) + dtime
  692. stretch = stretch + dstretch
  693. end do
  694. close(11)
  695. write(*, '(A)') 'Biaxial -> results/biaxial.dat'
  696. ! ========================== PURE SHEAR ==========================
  697. stress = zero; statev = zero; time = zero
  698. dfgrd1 = zero; dfgrd1(1,1) = one; dfgrd1(2,2) = one; dfgrd1(3,3) = one
  699. gamma_val = -{gamma_max}d0
  700. open(unit=12, file='results/shear.dat', status='replace')
  701. do i = 1, nsteps
  702. dfgrd1(1,2) = gamma_val
  703. dfgrd1(2,1) = gamma_val
  704. call umat(stress, statev, ddsdde, sse, spd, scd, rpl, ddsddt, &
  705. drplde, drpldt, stran, dstran, time, dtime, temp, dtemp, &
  706. predef, dpred, cmname, ndi, nshr, ntens, nstatev, props, &
  707. nprops, coords, drot, pnewdt, celent, dfgrd0, dfgrd1, &
  708. noel, npt, layer, kspt, kstep, kinc)
  709. write(12, '(3ES20.10)') time(1), gamma_val, stress(4)
  710. time(1) = time(1) + dtime
  711. gamma_val = gamma_val + dgamma
  712. end do
  713. close(12)
  714. write(*, '(A)') 'Shear -> results/shear.dat'
  715. ! ======================== SIMPLE SHEAR ========================
  716. stress = zero; statev = zero; time = zero
  717. dfgrd1 = zero; dfgrd1(1,1) = one; dfgrd1(2,2) = one; dfgrd1(3,3) = one
  718. gamma_val = -{gamma_max}d0
  719. open(unit=13, file='results/simple_shear.dat', status='replace')
  720. do i = 1, nsteps
  721. dfgrd1(1,2) = gamma_val
  722. call umat(stress, statev, ddsdde, sse, spd, scd, rpl, ddsddt, &
  723. drplde, drpldt, stran, dstran, time, dtime, temp, dtemp, &
  724. predef, dpred, cmname, ndi, nshr, ntens, nstatev, props, &
  725. nprops, coords, drot, pnewdt, celent, dfgrd0, dfgrd1, &
  726. noel, npt, layer, kspt, kstep, kinc)
  727. write(13, '(3ES20.10)') time(1), gamma_val, stress(4)
  728. time(1) = time(1) + dtime
  729. gamma_val = gamma_val + dgamma
  730. end do
  731. close(13)
  732. write(*, '(A)') 'Simple sh -> results/simple_shear.dat'
  733. end program test_umat
  734. ! Stub for ABAQUS-provided routine (standalone builds only)
  735. subroutine getoutdir(outdir, lenoutdir)
  736. implicit none
  737. character(len=256), intent(out) :: outdir
  738. integer, intent(out) :: lenoutdir
  739. outdir = '.'
  740. lenoutdir = 1
  741. end subroutine getoutdir
  742. """
  743. (outdir / "test_umat.f90").write_text(code)
  744. def generate_makefile(outdir, elem=None):
  745. uel_all = " test_uel" if elem else ""
  746. mk = f"""\
  747. FC = gfortran
  748. FFLAGS = -O2 -ffree-form
  749. all: test_umat{uel_all}
  750. test_umat: umat.f90 test_umat.f90
  751. \t$(FC) $(FFLAGS) -o $@ $^
  752. run: test_umat
  753. \t./test_umat
  754. """
  755. if elem:
  756. mk += """
  757. test_uel: uel.f90 test_uel.f90
  758. \t$(FC) $(FFLAGS) -o $@ $^
  759. run_uel: test_uel
  760. \t./test_uel
  761. """
  762. mk += """
  763. clean:
  764. \trm -f test_umat test_uel *.mod
  765. \trm -rf results
  766. """
  767. (outdir / "Makefile").write_text(mk)
  768. def generate_abaqus_dir(outdir, cfg):
  769. abq = outdir / "abaqus"
  770. abq.mkdir(exist_ok=True)
  771. props = build_props(cfg)
  772. nprops = len(props)
  773. nstatev = compute_nstatev(cfg)
  774. # --- cube.inp ---
  775. cube = """\
  776. ** Unit cube, single C3D8 element
  777. *Node, nset=all_nodes
  778. 1, 1., 1., 1.
  779. 2, 1., 0., 1.
  780. 3, 1., 1., 0.
  781. 4, 1., 0., 0.
  782. 5, 0., 1., 1.
  783. 6, 0., 0., 1.
  784. 7, 0., 1., 0.
  785. 8, 0., 0., 0.
  786. *Element, type=C3D8, elset=main_element
  787. 1, 5, 6, 8, 7, 1, 2, 4, 3
  788. *Nset, nset=Set-1, generate
  789. 2, 8, 2
  790. *Nset, nset=Set-2, generate
  791. 1, 7, 2
  792. *Nset, nset=Set-3
  793. 1, 2, 5, 6
  794. *Nset, nset=Set-4, generate
  795. 5, 8, 1
  796. *Nset, nset=Set-5
  797. 2, 4, 6, 8
  798. *Nset, nset=Set-6
  799. 3, 4, 7, 8
  800. *Nset, nset=Set-7, generate
  801. 1, 4, 1
  802. *Nset, nset=Set-8
  803. 1,3
  804. *Nset, nset=Set-9
  805. 6,8
  806. *Elset, elset=Surf
  807. 1,
  808. *Surface, type=ELEMENT, name=Surf-1
  809. Surf, S1
  810. *Surface, type=ELEMENT, name=Surf-2
  811. Surf, S2
  812. *Surface, type=ELEMENT, name=Surf-3
  813. Surf, S3
  814. *Surface, type=ELEMENT, name=Surf-4
  815. Surf, S4
  816. *Surface, type=ELEMENT, name=Surf-5
  817. Surf, S5
  818. *Surface, type=ELEMENT, name=Surf-6
  819. Surf, S6
  820. *INCLUDE, file=sec.inp
  821. *Step, name=static, nlgeom=Yes, inc=200
  822. *Static
  823. 0.01, 1., 1e-05, 0.1
  824. *INCLUDE, file=bcs_uni.inp
  825. *OUTPUT,FIELD,VARIABLE=PRESELECT,FREQ=1
  826. *ELEMENT OUTPUT, elset=main_element
  827. SDV
  828. *OUTPUT,HISTORY,VARIABLE=PRESELECT,FREQ=1
  829. *End Step
  830. """
  831. (abq / "cube.inp").write_text(cube)
  832. # --- sec.inp ---
  833. sdv_lines = f"{nstatev},\n1, DET, \"DET\""
  834. if cfg["damage_type"] > 0:
  835. sdv_lines += "\n2, DMG, \"damage\"\n3, MAXSEF, \"max SEF\""
  836. sec = f"""\
  837. *Solid Section, elset=main_element, material=UD
  838. *Material, name=UD
  839. *User Material, constants={nprops}
  840. {fmt_props_abaqus(props)}
  841. *DEPVAR
  842. {sdv_lines}
  843. """
  844. (abq / "sec.inp").write_text(sec)
  845. # --- bcs_uni.inp ---
  846. bcs_uni = """\
  847. *Boundary
  848. Set-3, ZSYMM
  849. *Boundary
  850. Set-4, XSYMM
  851. *Boundary
  852. Set-1, YSYMM
  853. *Boundary, type=displacement
  854. Set-2, 2,2, 0.6
  855. """
  856. (abq / "bcs_uni.inp").write_text(bcs_uni)
  857. # --- bcs_bi.inp ---
  858. bcs_bi = """\
  859. *Boundary
  860. Set-1, YSYMM
  861. *Boundary
  862. Set-3, ZSYMM
  863. *Boundary
  864. Set-4, XSYMM
  865. *Boundary
  866. Set-2, 2,2, 0.6
  867. *Boundary
  868. Set-7, 1,1, 0.6
  869. """
  870. (abq / "bcs_bi.inp").write_text(bcs_bi)
  871. # --- bcs_sh.inp ---
  872. bcs_sh = """\
  873. *Boundary
  874. Set-3, ZSYMM
  875. *Boundary
  876. Set-1, 1,2, 0.0
  877. *Boundary
  878. Set-2, 1,1, 0.6
  879. Set-2, 2,2, 0.0
  880. """
  881. (abq / "bcs_sh.inp").write_text(bcs_sh)
  882. # --- run.sh ---
  883. run_sh = """\
  884. #!/bin/bash
  885. # Run ABAQUS single-element test
  886. # Usage: ./run.sh [bcs_file]
  887. # ./run.sh # uniaxial (default)
  888. # ./run.sh bcs_bi.inp # biaxial
  889. # ./run.sh bcs_sh.inp # shear
  890. BCS=${1:-bcs_uni.inp}
  891. # Update the included BCS file
  892. sed -i "s/INCLUDE, file=bcs_.*/INCLUDE, file=${BCS}/" cube.inp
  893. abaqus job=cube user=../umat.f90 interactive
  894. """
  895. run_path = abq / "run.sh"
  896. run_path.write_text(run_sh)
  897. run_path.chmod(run_path.stat().st_mode | stat.S_IEXEC)
  898. # --- Main ---------------------------------------------------------------------
  899. def list_models():
  900. print("""
  901. Available model types:
  902. ISO_TYPE (isotropic):
  903. 0 None
  904. 1 Neo-Hookean params: C10
  905. 2 Mooney-Rivlin params: C10, C01
  906. 3 Ogden (N-term) params: N, mu1, alpha1, ..., muN, alphaN
  907. 4 Humphrey exponential params: C10, C01
  908. ANISO_TYPE (anisotropic, per fiber family):
  909. 0 None
  910. 1 HGO (dispersed) params: K1, K2, kappa, fiber_x, fiber_y, fiber_z
  911. 2 Humphrey fiber params: K1, K2, fiber_x, fiber_y, fiber_z
  912. 3 HGO (AI discrete) params: K1, K2, bdisp, factor, fiber_x, fiber_y, fiber_z
  913. 4 Humphrey (AI discrete) params: K1, K2, bdisp, factor, fiber_x, fiber_y, fiber_z
  914. 5 Humphrey + activation params: K1, K2, fiber_x, fiber_y, fiber_z, T0M
  915. NETWORK_TYPE:
  916. 0 None
  917. 1 Affine (RW) params: PHI, N, B_orient, EFI, pdir(3),
  918. L, R0F, mu0, beta, B0, lambda0, R0C, ETAC
  919. 2 Non-affine (RW) params: PHI, N, B_orient, EFI, PP,
  920. L, R0F, mu0, beta, B0, lambda0, R0C, ETAC
  921. 3 Mixed (AI) params: PHI, N_naff, PP, N_aff, B_orient, EFI, factor, pdir(3),
  922. L, R0F, mu0, beta, B0, lambda0, R0C, ETAC
  923. 4 Contractile (AI) params: PHI, N, B_orient, EFI, FRIC, FFMAX, factor, pdir(3),
  924. L, R0F, mu0, beta, B0, lambda0, R0C, ETAC, KCH(7)
  925. 5 Affine (AI) params: PHI, N, B_orient, EFI, factor, pdir(3),
  926. L, R0F, mu0, beta, B0, lambda0, R0C, ETAC
  927. 6 Non-affine (AI) params: PHI, N, PP, factor,
  928. L, R0F, mu0, beta, B0, lambda0, R0C, ETAC
  929. DAMAGE_TYPE:
  930. 0 None
  931. 1 Sigmoid params: beta_d, psi_half
  932. VISCO (per Maxwell branch, max 3):
  933. params: tau, theta (per branch)
  934. Example configs: """ + ", ".join(EXAMPLES.keys()))
  935. def sphere_quadrature(n=60):
  936. """Quasi-uniform unit-sphere quadrature in the format UEXTERNALDB reads
  937. ('x y z weight' per line, weights = 1/n). Fibonacci spiral; a generic
  938. orientation quadrature for the RW network types (1, 2). Replace with a
  939. higher-order spherical design for production accuracy if needed."""
  940. import math
  941. ga = math.pi * (3.0 - math.sqrt(5.0))
  942. w = 1.0 / n
  943. lines = []
  944. for i in range(n):
  945. z = 1.0 - 2.0 * (i + 0.5) / n
  946. r = math.sqrt(max(0.0, 1.0 - z * z))
  947. th = ga * i
  948. lines.append(f"{r*math.cos(th):.13f} {r*math.sin(th):.13f} {z:.13f} {w:.13f}")
  949. return "\n".join(lines) + "\n"
  950. def generate_quadrature(outdir, cfg):
  951. """Ship a sphere quadrature for the RW network types (1, 2), which read it
  952. via UEXTERNALDB. AI types (3-6) integrate over an icosahedron and need none."""
  953. if cfg["network_type"] in (1, 2):
  954. content = sphere_quadrature(60)
  955. (outdir / "sphere_int60c.inp").write_text(content)
  956. (outdir / "abaqus" / "sphere_int60c.inp").write_text(content)
  957. def generate(cfg):
  958. name = cfg["name"]
  959. outdir = SCRIPT_DIR / name
  960. outdir.mkdir(exist_ok=True)
  961. elem = element_cfg(cfg)
  962. generate_umat_f90(outdir)
  963. generate_aba_param(outdir)
  964. generate_test_driver(outdir, cfg)
  965. generate_makefile(outdir, elem)
  966. generate_abaqus_dir(outdir, cfg)
  967. generate_quadrature(outdir, cfg)
  968. if elem:
  969. generate_uel_f90(outdir, cfg, elem)
  970. generate_uel_test_driver(outdir, cfg, elem)
  971. generate_uel_abaqus(outdir, cfg, elem)
  972. # Save the config for reproducibility
  973. (outdir / "config.json").write_text(json.dumps(cfg, indent=2) + "\n")
  974. nprops = len(build_props(cfg))
  975. nstatev = compute_nstatev(cfg)
  976. print(f"\nGenerated material: {name}/")
  977. print(f" umat.f90 Concatenated UMAT source ({nprops} PROPS, {nstatev} STATEV)")
  978. print(f" test_umat.f90 Standalone test driver")
  979. print(f" Makefile Build & run: make run")
  980. print(f" aba_param.inc ABAQUS include")
  981. print(f" config.json Configuration (for regeneration)")
  982. print(f" abaqus/ ABAQUS single-element test")
  983. print(f" cube.inp C3D8 mesh + step definition")
  984. print(f" sec.inp *User Material card")
  985. print(f" bcs_uni.inp Uniaxial boundary conditions")
  986. print(f" bcs_bi.inp Biaxial boundary conditions")
  987. print(f" bcs_sh.inp Shear boundary conditions")
  988. print(f" run.sh ABAQUS submission script")
  989. if elem:
  990. nvars = elem["nint"] * nstatev
  991. print(f" uel.f90 User element + material library "
  992. f"(U3, {elem['nint']}-pt, F-bar={'on' if elem['fbar'] else 'off'}, Variables={nvars})")
  993. print(f" test_uel.f90 Standalone single-element driver")
  994. print(f" abaqus/uel_cube.inp + run_uel.sh ABAQUS UEL test")
  995. print(f"\nStandalone test: cd {name} && make run")
  996. if elem:
  997. print(f"UEL test: cd {name} && make run_uel")
  998. print(f"ABAQUS test: cd {name}/abaqus && ./run.sh")
  999. def main():
  1000. parser = argparse.ArgumentParser(
  1001. description="Generate a self-contained UMAT material law directory."
  1002. )
  1003. parser.add_argument("config", nargs="?", help="JSON configuration file")
  1004. parser.add_argument("--example", metavar="NAME",
  1005. help="Generate from built-in example: " + ", ".join(EXAMPLES.keys()))
  1006. parser.add_argument("--list", action="store_true", help="List available model types")
  1007. parser.add_argument("--uel", action="store_true",
  1008. help="Also emit a user element (uel.f90 + test_uel.f90 + ABAQUS "
  1009. "UEL deck) driven by this material; configs may instead "
  1010. "carry an explicit \"element\" block")
  1011. args = parser.parse_args()
  1012. if args.list:
  1013. list_models()
  1014. return
  1015. if args.example:
  1016. if args.example not in EXAMPLES:
  1017. print(f"Unknown example: {args.example}")
  1018. print(f"Available: {', '.join(EXAMPLES.keys())}")
  1019. sys.exit(1)
  1020. cfg = dict(EXAMPLES[args.example])
  1021. if args.uel:
  1022. cfg.setdefault("element", {})
  1023. generate(cfg)
  1024. return
  1025. if args.config:
  1026. with open(args.config) as f:
  1027. cfg = json.load(f)
  1028. if args.uel:
  1029. cfg.setdefault("element", {})
  1030. generate(cfg)
  1031. return
  1032. parser.print_help()
  1033. if __name__ == "__main__":
  1034. main()

generate.py at commit e597e91, no license · at the source

Overview

Authors: Luís Pacheco1,2, Marco Parente1,2, João Ferreira1
  1. Department of Mechanical Engineering, Faculty of Engineering, University of Porto, R. Dr. Roberto Frias, 4200-465 Porto, Portugal
  2. Institute of Mechanical Engineering and Industrial Management, R. Dr. Roberto Frias, 4200-465 Porto, Portugal
Institutions: Universidade do Porto (Portugal)
Journal: Biomechanics and modeling in mechanobiology, volume 25, issue 3, article 52
Dates: received 9 December 2025; accepted 23 March 2026; published online 3 June 2026; in print 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1007/s10237-026-02067-5 · PMID 42234214 · PMCID PMC13234050 · OpenAlex W4417483186
Open access: hybrid, a free copy (OpenAlex)
Status: code verified
Categories: computational modeling (no new data) (modality), none (in silico) (organism), computational (subfield)
Methods: Statistics
Keywords: Actin networks, Continuum mechanics, Finite elements, Sparse polynomial chaos expansions, Uncertainty quantification
MeSH: Actins*, Cross-Linking Reagents*, Animals, Computer Simulation, Elasticity, Finite Element Analysis, Models, Biological, Monte Carlo Method, Stochastic Processes, Stress, Mechanical (* major topic)
Topic: Cellular Mechanics and Interactions (Cell Biology, Biochemistry, Genetics and Molecular Biology), according to OpenAlex
Funding: Universidade do Porto
Citations: not cited yet (Europe PMC); 65 references in the paper

Abstract

Filamentous actin (F-actin) constitutes the primary contributor to cell elasticity and structural integrity, forming dynamic, crosslinked networks in the actin cortex. Existing mechanical models for F-actin and crosslinked filament networks successfully describe filament- and network-level behavior, but are often limited in accounting for biological dynamic processes and inherent material uncertainty and variability. We develop a stochastic modeling framework that integrates Polynomial Chaos Expansion (PCE) surrogates using the Finite Element Method (FEM). These surrogates replace filament-scale equations for compliant crosslinked F-actin networks, efficiently enabling uncertainty quantification and sensitivity analysis of key material parameters. The first and second statistical moments from the PCE are incorporated into a micro-sphere network model and implemented via a user-defined material subroutine. Validation was performed against 10 000 Monte Carlo simulations (MCS) for each of four FEM test cases: three simple deformation modes applied to a unit length cubic element, and a thin gel layer under shear mimicking a parallel plate rheology setup. In every test, the surrogate predicts the expected value of relevant stress quantities at maximum deformation with under 1% relative error versus the MCS reference. Moreover, the surrogate captures the network’s variability as measured by second-order moments, demonstrating its ability to deliver rapid, statistically faithful predictions of both mean response and standard deviation in simple element tests and experimentally relevant rheology geometries. The proposed methodology provides a scalable route for incorporating intrinsic material variability into F-actin mechanical modeling, with implications for studying cell motility, division, and pathologies related to cytoskeletal remodeling.

Reproduced under the paper's license (CC BY), from the paper cited above.

Repository

Its files are read in the Code ↔ Paper reader above, with 3 matches between paragraphs and lines of code.

jpsferreira/UMAT-ABAQUS_library

License: none: the authors keep all their rights
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: e597e91cfd727b6d27f52389c7b99b90029d9c56, 12 June 2026
Languages: Fortran (585), Shell (63), Python (48), NEURON (15)
Size: 2,669 files, 711 scripts
Software Heritage: not archived
Found in: the text, “Methods”
Holds: README, environment (pyproject.toml, uv.lock), tests, documentation
Not found: license file, CITATION.cff, continuous integration
Tools: NumPy (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
712 files

Tracing map

Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.

What the map holds:

  • 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 711 scripts, each with its path and the digest of its content;
  • 3 matches between paragraphs of the paper and lines of the code (method lexical-v1);
  • neither the text of the paper nor the code itself.

Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.

Data

No dataset and no data link were found in the paper.

Data Availability

The data that support the findings of this study are available from the corresponding author upon reasonable request.

Reproduced under the paper's license (CC BY), from the paper cited above.

Versions

The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.

Version 2, 28 September 2026

  • Publisher: n/a → Springer Science+Business Media

Version 1, 27 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 3 authors, 5 keywords, 10 MeSH terms, 1 funder, 61 references.

Cite

This paper

Pacheco, L., Parente, M., & Ferreira, J. (2026). Sparse polynomial surrogates for F-actin networks with compliant crosslinkers. Biomechanics and modeling in mechanobiology, 25(3), 52. https://doi.org/10.1007/s10237-026-02067-5

BibTeX

@article{pacheco2026sparse,
author = {Pacheco, Luís and Parente, Marco and Ferreira, João},
title = {{Sparse polynomial surrogates for F-actin networks with compliant crosslinkers}},
journal = {Biomechanics and modeling in mechanobiology},
year = {2026},
month = jun,
volume = {25},
number = {3},
pages = {52},
publisher = {Springer Science+Business Media},
issn = {1617-7959},
doi = {10.1007/s10237-026-02067-5},
url = {https://doi.org/10.1007/s10237-026-02067-5},
pmid = {42234214},
pmcid = {PMC13234050}
}

RIS

TY - JOUR
AU - Pacheco, Luís
AU - Parente, Marco
AU - Ferreira, João
TI - Sparse polynomial surrogates for F-actin networks with compliant crosslinkers
T2 - Biomechanics and modeling in mechanobiology
J2 - Biomech Model Mechanobiol
PY - 2026
DA - 2026/06/03
VL - 25
IS - 3
SP - 52
SN - 1617-7959
PB - Springer Science+Business Media
DO - 10.1007/s10237-026-02067-5
UR - https://doi.org/10.1007/s10237-026-02067-5
LA - en
ER -

CSL-JSON

{
"id": "10.1007/s10237-026-02067-5",
"type": "article-journal",
"title": "Sparse polynomial surrogates for F-actin networks with compliant crosslinkers",
"container-title": "Biomechanics and modeling in mechanobiology",
"author": [
{
"family": "Pacheco",
"given": "Luís"
},
{
"family": "Parente",
"given": "Marco"
},
{
"family": "Ferreira",
"given": "João"
}
],
"container-title-short": "Biomech Model Mechanobiol",
"volume": "25",
"issue": "3",
"page": "52",
"DOI": "10.1007/s10237-026-02067-5",
"PMID": "42234214",
"PMCID": "PMC13234050",
"ISSN": "1617-7959",
"publisher": "Springer Science+Business Media",
"URL": "https://doi.org/10.1007/s10237-026-02067-5",
"language": "en",
"issued": {
"date-parts": [
[
2026,
6,
3
]
]
}
}

The tracing map gets a citation of its own once an author has validated it and it has a DOI.

Similar papers

The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.

[1] doi:10.1038/s41467-026-76045-x [code]
A manufacturability-informed topology framework for AI-guided design of fibrous network materials.
Journal: Nature communications
In common: NumPy, 2 references
[2] doi:10.1002/2211-5463.70302
KDAC6 alters cell morphology and motility through positive and negative modulation of F-actin distribution.
Journal: FEBS open bio
In common: 2 references
[3] doi: [code]
Going deeper with morphologically detailed neural networks by simulation-based gradient propagation
Journal: Frontiers in computational neuroscience
In common: NumPy, none (in silico), computational, computational modeling (no new data)
[4] doi:10.1038/s41526-026-00644-7 [code]
A computational model of altered neuronal activity in altered gravity.
Journal: NPJ microgravity
In common: NumPy, none (in silico), computational, computational modeling (no new data)
[5] doi:10.1371/journal.pcbi.1014458 [code]
Neuronal excitability and parameter variability in the Hodgkin-Huxley model.
Journal: PLoS computational biology
In common: NumPy, none (in silico), computational, computational modeling (no new data)
[6] doi:10.1038/s41598-026-51212-8 [code]
Uncertainty aware machine learning for bridging simulation and experiment in high throughput materials characterization.
Journal: Scientific reports
In common: NumPy, none (in silico), computational, computational modeling (no new data)
[7] doi:10.1186/s12860-026-00584-w
A role for PaxB in regulating blebbing: experimental insights and theoretical perspectives from Dictyostelium discoideum.
Journal: BMC molecular and cell biology
In common: none (in silico), 1 reference
[8] doi:10.1371/journal.pcbi.1014617 [code]
An in silico framework for dissecting the mechanistic origins of in vivo recorded neuronal activity.
Journal: PLoS computational biology
In common: none (in silico), computational, computational modeling (no new data)
[9] doi:10.1007/s11571-026-10522-3 [code]
Acetylcholine enhances deviance detection in Hodgkin-Huxley neuronal networks.
Journal: Cognitive neurodynamics
In common: none (in silico), computational, computational modeling (no new data)
[10] doi:10.3389/fncom.2026.1799705
Structural synaptogenesis superior to functional modulation in a pruning-based recurrent network model of OCD.
Journal: Frontiers in computational neuroscience
In common: none (in silico), computational, computational modeling (no new data)

Contribute

The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.

Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.

Request its removal

To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).

Discussion, reproductions, activity

Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.

Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.

Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.