Skip to content

Commit 617d506

Browse files
hugtalbothugtalbot
andauthored
[examples] Add a thermo-elastic example (#204)
* [examples] Add a thermo-elastic example * minor comment to remove * Get compatible with #198 and add legend * Remove option of topo changes * Update scene further to recently introduced templates in Mass * avoid using strings * update scene with imgui, linkpath etc --------- Co-authored-by: hugtalbot <hugo.talbot@inria.fr>
1 parent 4fb7e89 commit 617d506

File tree

1 file changed

+115
-0
lines changed

1 file changed

+115
-0
lines changed

examples/thermoelasticity.py

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# Required import for python
2+
import Sofa
3+
import numpy as np
4+
5+
6+
def main():
7+
import SofaRuntime
8+
import Sofa.Gui
9+
import SofaImGui
10+
11+
root = Sofa.Core.Node("root")
12+
createScene(root)
13+
Sofa.Simulation.init(root)
14+
15+
Sofa.Gui.GUIManager.Init("myscene", "imgui")
16+
Sofa.Gui.GUIManager.createGUI(root, __file__)
17+
Sofa.Gui.GUIManager.SetDimension(1080, 600)
18+
Sofa.Gui.GUIManager.MainLoop(root)
19+
Sofa.Gui.GUIManager.closeGUI()
20+
21+
22+
def createScene(root):
23+
root.gravity=[0, -9.81, 0]
24+
root.dt=0.01
25+
root.bbox=[[-0.5, -0.5, -0.5], [0.5, 0.5, 0.5]]
26+
27+
root.addObject('DefaultAnimationLoop')
28+
root.addObject('DefaultVisualManagerLoop')
29+
root.addObject('RequiredPlugin', pluginName=['Sofa.Component.Constraint.Projective', 'Sofa.Component.Diffusion', 'Sofa.Component.Engine.Select',
30+
'Sofa.Component.LinearSolver.Direct', 'Sofa.Component.LinearSolver.Iterative', 'Sofa.Component.Mass', 'Sofa.Component.ODESolver.Backward', 'Sofa.Component.SolidMechanics.FEM.Elastic',
31+
'Sofa.Component.Topology.Container.Dynamic', 'Sofa.Component.Topology.Container.Grid', 'Sofa.Component.Topology.Mapping', 'Sofa.Component.Visual', 'Sofa.GL.Component.Engine', 'Sofa.GL.Component.Rendering2D',
32+
'Sofa.GL.Component.Rendering3D', 'Sofa.Component.StateContainer', 'Sofa.Component.Mapping.Linear'])
33+
root.addObject('VisualStyle', displayFlags="hideCollisionModels showVisualModels hideForceFields showBehaviorModels")
34+
35+
gridGenerator = root.addObject('RegularGridTopology', name="gridGenerator", nx=16, ny=6, nz=6, xmin=0, xmax=1, ymin=0, ymax=0.2, zmin=0, zmax=0.2)
36+
root.addObject("OglColorMap", legendTitle="A-dimensional temperature", legendOffset=[60,0], legendSize=20, min=0, max=1, showLegend=True, colorScheme="Blue to Red")
37+
38+
39+
tetraTopo = root.addChild('TetraTopology')
40+
tetrahedraContainer = tetraTopo.addObject("TetrahedronSetTopologyContainer", name="tetContainer", tags="3dgeometry")
41+
tetraTopo.addObject("TetrahedronSetTopologyModifier", name="Modifier")
42+
tetraTopo.addObject("Hexa2TetraTopologicalMapping", input=gridGenerator.linkpath, output=tetrahedraContainer.linkpath)
43+
tetraTopo.addObject("MechanicalObject", name="tetraO", position="@../gridGenerator.position", tags="3dgeometry")
44+
tetraTopo.addObject("BoxROI", name="BoundaryCondition", box=[-0.01, -0.01, -0.01, 0.01, 0.21, 0.21])
45+
46+
47+
meca = tetraTopo.addChild("Mechanics")
48+
meca.addObject("EulerImplicitSolver", name="Euler Impl IntegrationScheme")
49+
meca.addObject("SparseLDLSolver", name="LDL LinearSolver", template="CompressedRowSparseMatrixMat3x3d")
50+
meca.addObject("TetrahedronSetTopologyContainer", name="tetContainer", src=tetrahedraContainer.linkpath)
51+
meca.addObject("TetrahedronSetGeometryAlgorithms", name="tetGeometry", template="Vec3d")
52+
elasticMO = meca.addObject("MechanicalObject", name="MO", position="@../tetraO.position")
53+
meca.addObject("MeshMatrixMass", template="Vec3d,Vec3d", name="Mass", massDensity=100, topology=tetrahedraContainer.linkpath, geometryState="@MO")
54+
meca.addObject("FixedConstraint", name="FixedBoundaryCondition", indices="@../BoundaryCondition.indices")
55+
56+
#initialization of the vector multiplying the local Young's modulus (need to activate updateStiffness=True)
57+
initVector = np.full((1, 576), 1.0)
58+
meca.addObject("TetrahedronFEMForceField", name="LinearElasticity", youngModulus=3e5, poissonRatio=0.4, computeVonMisesStress=1, showVonMisesStressPerElement=True, localStiffnessFactor=initVector, updateStiffness=True)
59+
60+
61+
thermo = tetraTopo.addChild("Thermodynamics")
62+
thermo.addObject("EulerImplicitSolver", name="Euler Impl IntegrationScheme", firstOrder=True)
63+
thermo.addObject("CGLinearSolver", name="Conjugate Gradient", iterations="1000", tolerance=1.0e-10, threshold=1.0e-30)
64+
thermo.addObject("TetrahedronSetTopologyContainer", name="tetContainer", src="@../tetContainer")
65+
thermo.addObject("TetrahedronSetGeometryAlgorithms", name="tetGeometry", template="Vec3d")
66+
thermo.addObject("MechanicalObject", name="Temperatures", template="Vec1d", size=576, showObject=True)
67+
thermo.addObject("MeshMatrixMass", template="Vec1d,Vec3d", name="Conductivity", massDensity=1.0, topology=tetrahedraContainer.linkpath, geometryState=elasticMO.linkpath)
68+
thermo.addObject("FixedConstraint", name="Heating", indices=495)
69+
thermo.addObject("TetrahedronDiffusionFEMForceField", name="DiffusionForceField", template="Vec1d", constantDiffusionCoefficient=0.05, tagMechanics="3dgeometry", mstate="@Temperatures")
70+
71+
thermoVisu = thermo.addChild("Visu")
72+
thermoVisu.addObject("TextureInterpolation", template="Vec1d", name="EngineInterpolation", input_states="@../Temperatures.position", input_coordinates="@../../Mechanics/MO.position", min_value=0., max_value=1., manual_scale=True , drawPotentiels=False, showIndicesScale=5e-05)
73+
thermoVisu.addObject("OglModel", template="Vec3d", name="oglPotentiel", handleDynamicTopology="0", texcoords="@EngineInterpolation.output_coordinates" ,texturename="textures/heatColor.bmp", scale3d=[1, 1, 1], material="Default Diffuse 1 1 1 1 0.5 Ambient 1 1 1 1 0.3 Specular 0 0.5 0.5 0.5 1 Emissive 0 0.5 0.5 0.5 1 Shininess 0 45 No texture linked to the material No bump texture linked to the material ")
74+
thermoVisu.addObject("IdentityMapping", input=elasticMO.linkpath, output="@oglPotentiel")
75+
76+
# Add the controller
77+
root.addObject(ThermoMecaController(name = "MultiPhysicsController", ThermalObject=thermo.Temperatures, MechanicalForceField=meca.LinearElasticity))
78+
79+
return root
80+
81+
82+
class ThermoMecaController(Sofa.Core.Controller):
83+
def __init__(self, *args, **kwargs):
84+
# These are needed (and the normal way to override from a python class)
85+
Sofa.Core.Controller.__init__(self, *args, **kwargs)
86+
# Recover the pointers to the thermal object and the ForceField responsible of the linear elastic constitutive law
87+
self.ThermalObject = kwargs.get("ThermalObject")
88+
self.MechanicalForceField = kwargs.get("MechanicalForceField")
89+
90+
def onAnimateEndEvent(self, event):
91+
time = self.getContext().getTime()
92+
93+
self.temperatures = self.ThermalObject.position.array()
94+
95+
if time > 0.5 :
96+
97+
# Make the Young's modulus related to the temperature: E = E_init / (1 + 50*Temperature)
98+
with self.MechanicalForceField.localStiffnessFactor.writeableArray() as wa:
99+
for i in range(576) :
100+
wa[i] = 1.0 / ( 1.+ 50.*self.temperatures[i][0] )
101+
102+
#Enforce temperature of the node 495 (extremity of the bar)
103+
# Heating phase until t = 4
104+
if time < 4.0 :
105+
with self.ThermalObject.position.writeableArray() as wt:
106+
wt[495][0] = 1.0
107+
# Cooling down phase when t > 4
108+
else :
109+
with self.ThermalObject.position.writeableArray() as wt:
110+
wt[495][0] = 0.0
111+
112+
113+
# Function used only if this script is called from a python environment
114+
if __name__ == '__main__':
115+
main()

0 commit comments

Comments
 (0)