diff --git a/cxxfem/src/femSolver.cpp b/cxxfem/src/femSolver.cpp index 57f33c6e0363a5eb197dc1c84b2992aef9d0ec8d..e2a92ad51be6f047046897d5548e724e7b2858f9 100644 --- a/cxxfem/src/femSolver.cpp +++ b/cxxfem/src/femSolver.cpp @@ -67,8 +67,8 @@ void Solver::solve() create_partition(); fill_partition(); - fill_rhs(); fill_lhs(); + fill_rhs(); apply_bc(); solve_static(); @@ -206,11 +206,11 @@ void Solver::fill_partition() std::cout << "\telasped = " << timers["fill_partition"].read() << "s\n"; } -/// fill right-hand side of the system of equations +/// fill left-hand side of the system of equations -void Solver::fill_rhs() +void Solver::fill_lhs() { - timers["fill_rhs"].start(); + timers["fill_lhs"].start(); std::cout << "assembly of LHS...\n"; @@ -527,15 +527,15 @@ void Solver::fill_rhs() for (size_t j = 0; j < Kijv[i].size(); ++j) delete Kijv[i][j]; - timers["fill_rhs"].stop(); - std::cout << "\telasped = " << timers["fill_rhs"].read() << "s\n"; + timers["fill_lhs"].stop(); + std::cout << "\telasped = " << timers["fill_lhs"].read() << "s\n"; } /// compute right-hand side 'f' (sources) -void Solver::fill_lhs() +void Solver::fill_rhs() { - timers["fill_lhs"].start(); + timers["fill_rhs"].start(); std::cout << "assembly of RHS...\n"; @@ -627,8 +627,8 @@ void Solver::fill_lhs() } } - timers["fill_lhs"].stop(); - std::cout << "\telasped = " << timers["fill_lhs"].read() << "s\n"; + timers["fill_rhs"].stop(); + std::cout << "\telasped = " << timers["fill_rhs"].read() << "s\n"; } diff --git a/models/boneload.py b/models/boneload.py index 103b77324d5cd2cfe839d4c6c89d5cfcf87f733a..35c1387a30d5884fd528d6b275dae0e351e2103a 100644 --- a/models/boneload.py +++ b/models/boneload.py @@ -461,24 +461,37 @@ def create_loads(nodes, tris, total_force, target, method='T'): "adds the normal traction (i.e., pressure) that muscle fibers impose on the skull as they wrap around it." + method = 'D' (Directional-Traction Model) + all the nodal forces are applied along a prescribed direction + given by "target" see as a vector + method = 'N' (Normal-Traction Model) + all the nodal forces are applied along the normal to each triangle """ # options if method == 'U': - project = False methodtxt = 'Ad Hoc Uniform Traction Model' + project = False normal_comp = None elif method == 'T': + methodtxt = 'Tangential-Traction Model' project = True normal_comp = False - methodtxt = 'Tangential-Traction Model' elif method == 'T+N': + methodtxt = 'Tangential-Plus-Normal-Traction Model' project = True normal_comp = True - methodtxt = 'Tangential-Plus-Normal-Traction Model' + elif method == 'D': + methodtxt = 'Directional-Traction Model' + project = False + normal_comp = None + elif method == 'N': + methodtxt = 'Normal-Traction Model' + project = False + normal_comp = None else: raise Exception( - "unknown method: choose from 'uniform', 'tangential' or 'tangential+normal'") + f"unknown method ({method}): choose from 'U', 'T', 'T+N', 'D' or 'N'") if normal_comp: muscle = SurfMesh(nodes, tris, vertices=False) @@ -492,11 +505,17 @@ def create_loads(nodes, tris, total_force, target, method='T'): for tri in tris: p1, p2, p3 = [Pt(nodes[tri[i]]) for i in range(3)] centre = (p1+p2+p3)/3 # barycentre - e1 = p2-p1 # edge 1 - e2 = p3-p1 # edge 2 - direct = targetP-centre - direct.normalize() # direction of the traction (normalised) + e1 = p2 - p1 # edge 1 + e2 = p3 - p1 # edge 2 n = e1.cross(e2) # normal vector + if method == 'D': + direct = targetP # force direction is the same for all triangles + elif method == 'N': + direct = n # force direction is normal to the triangle + else: + direct = targetP-centre # force direction varies from 1 triangle to another + direct.normalize() # direction of the traction (normalised) + area2 = abs(n) # 2 x area area = area2/2 # triangle area total_area += area diff --git a/models/bonemodel2.py b/models/bonemodel2.py index 3f2634be0d67b11a173a42597f906144ab59d5d0..3e62d531e80a855a030a699c6b44dad57cc16103 100644 --- a/models/bonemodel2.py +++ b/models/bonemodel2.py @@ -63,6 +63,8 @@ def parms(d={}): } ] + p['loads'] = [] + # material properties p['Young'] = 17000. # [MPa] elastic modulus - bone: 17-20 GPa p['Poisson'] = 0.3 # [-] Poisson's ratio @@ -134,7 +136,6 @@ def solve(p={}): for fix in p['fixations']: create_group(fix['nodes'], nods_no, nods_pos, groups, fix['name']) - # -------------------------------------------------------------------------- # print('== ADDING MEDIUM...') print('') @@ -159,6 +160,15 @@ def solve(p={}): do_not_delete.append(fem.Neumann(pbl, int(gr.ntags[i]), 'Y', load.x[1]) ) # TODO: regler "dof=" qui marche pas do_not_delete.append(fem.Neumann(pbl, int(gr.ntags[i]), 'Z', load.x[2]) ) + # add Neumann conditions (loads) from external loads + for load in p['loads']: + name, nodes, tris, ntags = \ + create_group(load['nodes'], nods_no, nods_pos, groups, load['name']) + for ntag in ntags: + for i in range(3): + do_not_delete.append(fem.Neumann(pbl, int(ntag), 'XYZ'[i], load['values'][i]) ) + + # python only! # do_not_delete.append(fem.Dirichlet(pbl, "all", dof='T', val=0.0) ) diff --git a/models/dolicorhynchops/dolicorhynchops_10k_extloads.py b/models/dolicorhynchops/dolicorhynchops_10k_extloads.py new file mode 100644 index 0000000000000000000000000000000000000000..4812b3936c8c239d9be8f9eec61c4a4333f750bf --- /dev/null +++ b/models/dolicorhynchops/dolicorhynchops_10k_extloads.py @@ -0,0 +1,77 @@ +#! /usr/bin/env python3 +# -*- coding: utf-8 -*- +# Dolicorhynchops osborni FHSM VP404 +# 10k faces on the mandible surface + + +def parms(d={}): + p = {} + import os + path = os.path.join(os.path.dirname(__file__),'10k') + p['bone'] = f'{path}/mandible.stl' + p['muscles'] = [ + { + 'file': f'{path}/Lmuscle.stl', + 'force': 100., + # f'{path}/LmuscleF.off', + 'focalpt': [-100.1458893, -173.13895, 227.3909], + 'method': 'U' + }, + { + 'file': f'{path}/Rmuscle.off', + 'force': 100., + 'focalpt': f'{path}/RmuscleF.off', + 'method': 'T' + } + ] + p['fixations'] = [ + { + 'name': 'contact_pts', + 'nodes': [[-10.20362, -17.46838, -229.9061], + [-11.92466, 26.3042, -229.5354]], + 'direction': 'x' + }, + { + 'name': 'axis_pt1', + 'nodes': f'{path}/LTMJ.off', + 'direction': ['x', 'y', 'z'] + }, + { + 'name': 'axis_pt2', + 'nodes': [-8.716309, 79.13171, 233.8385], + 'direction': ['x', 'z'] + } + ] + + p['loads'] = [ + { + 'name': 'force1', + 'nodes': [-10.20362, -17.46838, -229.9061], + 'values': [0., 0., -100.] + }, + { + 'name': 'force2', + 'nodes': [-11.92466, 26.3042, -229.5354], + 'values': [0., 0., 100.] + } + ] + + + + # material properties + p['density'] = 1.850e-9 # [T/mm³] + p['Young'] = 20000. # [MPa] + p['Poisson'] = 0.3 # [-] + + p.update(d) + return p + + +def getMetafor(p={}): + import bonemodel as model + return model.getMetafor(parms(p)) + + +if __name__ == "__main__": + import models.bonemodel2 as model + model.solve(parms())