From 1683ffc974fc86a65a41a264b3b1906b2032f631 Mon Sep 17 00:00:00 2001 From: Tristan de Lataillade Date: Sat, 22 Jul 2017 04:37:21 +0100 Subject: [PATCH] Multi-body and moorings dynamics with Chrono for Proteus (#525) * [ADD] Chrono wrappers for multibody dynamics * MBD: chrono moorings coupling working * MBD: Chrono collision detection added * MBD: chrono wrappers refactoring; external forces for cable * MBD: chrono cable external forces updated * typo fix * Chrono testing version for force oscillation * [BUG] chrono: nans in parallel when using moorings * [BUG] nans on master processor using hydra * mbd: commented out debugging lines; corrected typo * mbd: choice between beam and cable elements for moorings * [FIX] chrono calculations on rank 1 in parallel * ChElementCableANCF used in chrono; prescribed sine motion added * mbd: added documentation (partial) * MBD: cythonised further; py headers in different files * MBD: added ISS coupling scheme * msh2simplex: update node flags with face flags (3D) * Isosurfaces output in .h5 files * NumericalSolution: fix fileprefix when using gmsh for 3D domain * MBD: kdtree parallel lookup fixed; WIP on coupling schemes * MBD: added Euler beams for cables, updated external forces * msh2simplex: removed flag assignment for triangle nodes in 2D * MBD: added separated mesh motion function (translation/rotation) * reintroduced MeshAdaptPUMI in setup.py * Cekees/mbd/chrono (#570) * re-enabled Nitsche adjoint for pinc * updated optimized pressure calculation for projection scheme * enabled optimized pressure solve for rans3p * enabled optimized pressure solve for rans3p * fixed mass flux for rotational form of pressure calc * worked on isotropic interface size field * fix 2D Darcy term and isotropic adaptive scheme * update stack with fixed zoltan build * updated hashstack (chrono) * removed irrlicht dependency * enable avx extensions by default for chrono --- .hashstack_default | 3 +- Makefile | 1 + proteus/BoundaryConditions.py | 2 + proteus/Isosurface.pyx | 34 +- proteus/MeshAdaptPUMI/SizeField.cpp | 32 +- proteus/MeshAdaptPUMI/cMeshAdaptPUMI.cpp | 2 +- proteus/MeshTools.py | 7 +- proteus/NumericalSolution.py | 16 +- proteus/SpatialTools.py | 107 +- proteus/mbd/ChMoorings.h | 1073 ++++++++++ proteus/mbd/ChRigidBody.h | 442 +++++ proteus/mbd/ChRigidBody.pyx | 2290 ++++++++++++++++++++++ proteus/mbd/ChronoHeaders.pxd | 238 +++ proteus/mbd/__init__.py | 0 proteus/mbd/pyChronoCore.pxd | 101 + proteus/mbd/pyChronoCore.pyx | 303 +++ proteus/mprans/BoundaryConditions.py | 12 +- proteus/mprans/MCorr.h | 2 +- proteus/mprans/MCorr.py | 5 +- proteus/mprans/RANS2P2D.h | 11 +- proteus/mprans/SpatialTools.py | 4 +- proteus/mprans/VOF.py | 2 + setup.py | 24 +- 23 files changed, 4657 insertions(+), 54 deletions(-) create mode 100644 proteus/mbd/ChMoorings.h create mode 100644 proteus/mbd/ChRigidBody.h create mode 100644 proteus/mbd/ChRigidBody.pyx create mode 100644 proteus/mbd/ChronoHeaders.pxd create mode 100644 proteus/mbd/__init__.py create mode 100644 proteus/mbd/pyChronoCore.pxd create mode 100644 proteus/mbd/pyChronoCore.pyx diff --git a/.hashstack_default b/.hashstack_default index 48888fdf66..e8efe6f3ef 100644 --- a/.hashstack_default +++ b/.hashstack_default @@ -1 +1,2 @@ -d3d27601f2de3c4538f11972f68973b142bdbc92 +5eb94cea715c2d5e1f8d2e90fd0a53c96a7a7df5 + diff --git a/Makefile b/Makefile index 82ce2119d1..b07b056e5d 100644 --- a/Makefile +++ b/Makefile @@ -230,6 +230,7 @@ install: profile $(shell find proteus -type f) $(wildcard *.py) proteus $(call howto) develop: proteus profile + -ln -sf ${PROTEUS}/${PROTEUS_ARCH}/lib64/* ${PROTEUS}/${PROTEUS_ARCH}/lib @echo "************************" @echo "Installing development version" @echo "************************" diff --git a/proteus/BoundaryConditions.py b/proteus/BoundaryConditions.py index 9f382197d4..1d1c06da80 100644 --- a/proteus/BoundaryConditions.py +++ b/proteus/BoundaryConditions.py @@ -22,6 +22,8 @@ def __init__(self, shape=None, name=None, b_or=None, b_i=0, nd=None): assert nd is not None, 'Shape or nd must be passed to BC' if b_or is not None: self._b_or = b_or[b_i] # array of orientation of all boundaries of shape + else: + self._b_or = None # @staticmethod # def newGlobalBC(name, default_value): diff --git a/proteus/Isosurface.pyx b/proteus/Isosurface.pyx index 99453cd3aa..3066b42f56 100644 --- a/proteus/Isosurface.pyx +++ b/proteus/Isosurface.pyx @@ -8,6 +8,7 @@ AuxiliaryVariables subclasses for extracting isosurfaces and contours """ from collections import defaultdict, OrderedDict from itertools import product +import os #from proteus.EGeometry import etriple, ecross, enorm, edot from mpi4py import MPI @@ -18,6 +19,7 @@ from numpy.linalg import norm from . import Comm from .AuxiliaryVariables import AV_base +from proteus import Profiling from .Profiling import logEvent as log from libc.math cimport sqrt @@ -104,6 +106,7 @@ class Isosurface(AV_base): self.format = format self.comm = Comm.get() self.writeBoundary = writeBoundary + self.fileprefix = 'isosurface' def attachModel(self, model, ar): """ Attach this isosurface to the given simulation model. @@ -128,7 +131,7 @@ class Isosurface(AV_base): self.u = fine_grid.u self.timeIntegration = fine_grid.timeIntegration self.nFrames = 0 - self.last_output = None + self.next_output = 0 self.writeSceneHeader() return self @@ -138,6 +141,7 @@ class Isosurface(AV_base): """ from collections import namedtuple self.fieldNames = [isosurface[0] for isosurface in self.isosurfaces] + print("ATTACHING TO HDF5 !!", self.fieldNames) self.elementNodesArray = h5.getNode("/elementsSpatial_Domain" + repr(step))[:] self.nodeArray = h5.getNode("/nodesSpatial_Domain" + repr(step))[:] @@ -149,7 +153,7 @@ class Isosurface(AV_base): self.isosurfaces[0][0] + repr(step))[:]) self.nFrames = step - self.last_output = None + self.next_output = 0 if step == 0: self.writeSceneHeader(cam) return self @@ -348,11 +352,30 @@ class Isosurface(AV_base): if self.format == 'pov': log("Writing pov frame " + repr(frame)) self.writeIsosurfaceMesh_povray(field, value, frame) + elif self.format == 'h5': + self.writeIsosurfaceMesh_h5(field, value, frame) elif self.format is None: pass else: log("Isosurface file format not recognized") + def writeIsosurfaceMesh_h5(self, field, value, frame): + import h5py + nodes = self.nodes[(field, value)] + elements = self.elements[(field, value)] + normals = self.normals[(field, value)] + normal_indices = self.normal_indices[(field, value)] + filename = os.path.join(Profiling.logDir, self.fileprefix+str(self.comm.rank())+'.h5') + if self.nFrames == 0: + f = h5py.File(filename, "w") + else: + f = h5py.File(filename, "a") + dset = f.create_dataset('nodes'+str(self.nFrames), data=nodes) + dset = f.create_dataset('elems'+str(self.nFrames), data=elements) + dset = f.create_dataset('normals'+str(self.nFrames), data=normals) + dset = f.create_dataset('normal_indices'+str(self.nFrames), data=normal_indices) + f.close() + def writeIsosurfaceMesh_povray(self, field, value, frame): """ Write the triangular mesh to a povray file @@ -517,8 +540,7 @@ vertex_vectors {""" return # check that gauge is ready to be sampled again - if (self.last_output is not None and - time < self.last_output + self.sampleRate): + if (time < self.next_output): return assert self.elementNodesArray.shape[1] == 4, \ "Elements have {0:d} vertices but algorithm is for tets".format( @@ -528,8 +550,8 @@ vertex_vectors {""" self.triangulateIsosurface(field, v) self.writeIsosurfaceMesh(field, v, self.nFrames) self.nFrames += 1 - if checkTime: - self.last_output = time + if checkTime and time != 0: + self.next_output += self.sampleRate def writeSceneHeader(self, cam = None): """ diff --git a/proteus/MeshAdaptPUMI/SizeField.cpp b/proteus/MeshAdaptPUMI/SizeField.cpp index e0c04537e1..b07cb71d85 100644 --- a/proteus/MeshAdaptPUMI/SizeField.cpp +++ b/proteus/MeshAdaptPUMI/SizeField.cpp @@ -17,13 +17,9 @@ static void SmoothField(apf::Field* f); thickness of refinement near the interface */ static double isotropicFormula(double phi, double hmin, double hmax) { - static double const epsilon = 0.02; - phi = fabs(phi); double size; - if (fabs(phi) < epsilon) + if (fabs(phi) < 5.0*hmin) size = hmin; - else if (phi < 3 * epsilon) - size = (hmin + hmax) / 2; else size = hmax; return size; @@ -43,8 +39,30 @@ int MeshAdaptPUMIDrvr::calculateSizeField() apf::setScalar(size_iso, v, 0, size); } m->end(it); - for(int i=0; i < 3; i++) - SmoothField(size_iso); + /* + If you just smooth then hmax will just diffuse into the hmin band + and you won't really get a band around phi=0 with uniform diameter + hmin. Instead, reset to hmin after each smooth within the band in + order to ensure the band uses hmin. Iterate on that process until + changes in the smoothed size are less than 50% of hmin. + */ + double err_h_max=hmax; + int its=0; + while (err_h_max > 0.5*hmin && its < 200) + { + its++; + SmoothField(size_iso); + err_h_max=0.0; + it = m->begin(0); + while ((v = m->iterate(it))) { + double phi = apf::getScalar(phif, v, 0); + double size_current = apf::getScalar(size_iso, v, 0); + double size = fmin(size_current,isotropicFormula(phi, hmin, hmax)); + err_h_max = fmax(err_h_max,fabs(size_current-size)); + apf::setScalar(size_iso, v, 0, size); + } + m->end(it); + } return 0; } diff --git a/proteus/MeshAdaptPUMI/cMeshAdaptPUMI.cpp b/proteus/MeshAdaptPUMI/cMeshAdaptPUMI.cpp index 56634c8225..4f5b1e21b4 100644 --- a/proteus/MeshAdaptPUMI/cMeshAdaptPUMI.cpp +++ b/proteus/MeshAdaptPUMI/cMeshAdaptPUMI.cpp @@ -351,7 +351,7 @@ int MeshAdaptPUMIDrvr::adaptPUMIMesh() size_iso = samSz::isoSize(m); } else if (size_field_config == "isotropic") - testIsotropicSizeField(); + calculateSizeField(); else { std::cerr << "unknown size field config " << size_field_config << '\n'; abort(); diff --git a/proteus/MeshTools.py b/proteus/MeshTools.py index ead35815ec..0f292ef27d 100644 --- a/proteus/MeshTools.py +++ b/proteus/MeshTools.py @@ -6429,7 +6429,7 @@ def msh2simplex(fileprefix, nd): switch = None switch_count = -1 logEvent('msh2simplex: getting nodes and elements') - for line in mshfile: + for i, line in enumerate(mshfile): if 'Nodes' in line: switch = 'nodes' switch_count = -1 @@ -6466,6 +6466,11 @@ def msh2simplex(fileprefix, nd): elif el_type == 2: # triangle triangle_nb += 1 triangles += [[triangle_nb, int(words[s]), int(words[s+1]), int(words[s+2]), flag]] + # update nodes flags + if nd == 3: + for i in range(3): + if nodes[int(words[s+i])-1][4] == 0: + nodes[int(words[s+i])-1][4] = flag elif el_type == 4: # tetrahedron tetrahedron_nb += 1 tetrahedra += [[tetrahedron_nb, int(words[s]), int(words[s+1]), int(words[s+2]), int(words[s+3]), flag]] diff --git a/proteus/NumericalSolution.py b/proteus/NumericalSolution.py index 87897cfe02..8434ccf72e 100644 --- a/proteus/NumericalSolution.py +++ b/proteus/NumericalSolution.py @@ -294,9 +294,9 @@ def __init__(self,so,pList,nList,sList,opts,simFlagsList=None): fileprefix = p.domain.geofile else: fileprefix = p.domain.polyfile - if comm.rank() == 0 and (p.genMesh or not (os.path.exists(p.domain.polyfile+".ele") and - os.path.exists(p.domain.polyfile+".node") and - os.path.exists(p.domain.polyfile+".face"))): + if comm.rank() == 0 and (p.genMesh or not (os.path.exists(fileprefix+".ele") and + os.path.exists(fileprefix+".node") and + os.path.exists(fileprefix+".face"))): if p.domain.use_gmsh is True: logEvent("Running gmsh to generate 3D mesh for "+p.name,level=1) gmsh_cmd = "time gmsh {0:s} -v 10 -3 -o {1:s} -format msh".format(fileprefix+'.geo', p.domain.geofile+'.msh') @@ -307,7 +307,7 @@ def __init__(self,so,pList,nList,sList,opts,simFlagsList=None): check_call("tetgen -Vfeen {0:s}.ele".format(fileprefix), shell=True) else: logEvent("Running tetgen to generate 3D mesh for "+p.name, level=1) - tetcmd = "tetgen -{0} {1}.poly".format(n.triangleOptions, p.domain.polyfile) + tetcmd = "tetgen -{0} {1}.poly".format(n.triangleOptions, fileprefix) logEvent("Calling tetgen on rank 0 with command %s" % (tetcmd,)) check_call(tetcmd, shell=True) logEvent("Done running tetgen") @@ -1239,7 +1239,7 @@ def calculateSolution(self,runName): # print "Min / Max residual %s / %s" %(lr.min(),lr.max()) self.nSequenceSteps = 0 - self.nSolveSteps = 0 + self.nSolveSteps=self.nList[0].adaptMesh_nSteps-1 for (self.tn_last,self.tn) in zip(self.tnList[:-1],self.tnList[1:]): logEvent("==============================================================",level=0) logEvent("Solving over interval [%12.5e,%12.5e]" % (self.tn_last,self.tn),level=0) @@ -1381,7 +1381,10 @@ def calculateSolution(self,runName): self.tCount+=1 for index,model in enumerate(self.modelList): self.archiveSolution(model,index,self.systemStepController.t_system_last) - + #can only handle PUMIDomain's for now + self.nSolveSteps += 1 + if(self.PUMI_estimateError()): + self.PUMI_adaptMesh() #end system step iterations if self.archiveFlag == ArchiveFlags.EVERY_USER_STEP: self.tCount+=1 @@ -1398,7 +1401,6 @@ def calculateSolution(self,runName): self.nSolveSteps += 1 if(self.PUMI_estimateError()): self.PUMI_adaptMesh() - logEvent("Finished calculating solution",level=3) for index,model in enumerate(self.modelList): diff --git a/proteus/SpatialTools.py b/proteus/SpatialTools.py index ede9a4f064..f2474a9456 100644 --- a/proteus/SpatialTools.py +++ b/proteus/SpatialTools.py @@ -32,6 +32,7 @@ from math import cos, sin, sqrt import math import sys +import copy import numpy as np from proteus import BoundaryConditions as bc from .Profiling import logEvent @@ -389,7 +390,7 @@ def __init__(self, domain, dim=(0., 0., 0.), coords=(0., 0., 0.), [-1., 0., 0.], [0., 0., 1.]]) self.regions = np.array([[x, y, z]]) - self.volumes = np.array([[[0, 1, 2, 3, 4, 5]]]) + self.volumes = [[[0, 1, 2, 3, 4, 5]]] # defining flags for boundary conditions self.boundaryTags = bt = {'z-': 1, 'y+': 2, @@ -481,7 +482,7 @@ def __init__(self, domain, radius, coords=(0.,0.,0.), barycenter=None, self.coords = np.array(coords) self.nSectors = nSectors self.constructShape() - self.volumes = np.array([[[i for i in range(len(self.facets))]]]) + self.volumes = [[[i for i in range(len(self.facets))]]] # defining flags for boundary conditions self.boundaryTags = bt = {'sphere': 1} self.BC = {'sphere': self.BC_class(shape=self, name='sphere')} @@ -784,6 +785,77 @@ def setDimensions(self, dim): [x-0.5*L, y+0.5*H]] self.volume = L*H +class Cylinder(Shape): + """ + Class to create a cylinder. + + Parameters + ---------- + domain: proteus.Domain.D_base + Domain class instance that hold all the geometrical informations and + boundary conditions of the shape. + radius: float + radius of cylinder. + height: float + height of cylinder. + nPoints: int + number of points to discretize circles of cylinder. + coords: Optional[array_like] + Coordinates of the centroid of the shape. + barycenter: Optional[array_like] + Coordinates of the barycenter. + """ + count = 0 + def __init__(self, domain, radius, height, nPoints, coords=(0.,0.,0.), barycenter=None): + super(Cylinder, self).__init__(domain, nd=3) + self.__class__.count += 1 + self.name = "Cylinder" + str(self.__class__.count) + self.radius = radius + self.height = height + self.coords = np.array(coords) + self.barycenter = np.array(barycenter) + self.nPoints = nPoints + self.constructShape() + + def constructShape(self): + h_offset = np.array([0., 0., self.height]) + arc = 2.*np.pi*self.radius/self.nPoints + ang = arc/self.radius + vert = [] + facets = [] + segs = [] + for i in range(0, self.nPoints): + vert += [[self.radius*cos(ang*i), + self.radius*sin(ang*i), + 0]] + if i > 0: + segs += [[i-1, i]] + segs += [[i, 0]] + segs_bottom = np.array(segs) + vert_bottom = np.array(vert) + facets += [[[i for i in range(0, len(vert_bottom))]]] + vert_top = np.array(vert)+h_offset + segs_top = np.array(segs)+len(vert) + nvb = len(vert_bottom) + facets += [[[i+nvb for i in range(0, len(vert_top))]]] + for i in range(len(vert_bottom)-1): + facets += [[[i, i+1, i+nvb+1, i+nvb]]] + facets += [[[i+1, 0, nvb, i+1+nvb]]] # last facet + self.vertices = np.vstack((vert_bottom, vert_top))-h_offset/2.+np.array(self.coords) + self.segments = np.vstack((segs_bottom, segs_top)) + self.segmentFlags = np.array([1 for i in range(len(segs_bottom))]+[2 for i in range(len(segs_top))]) + self.facets = facets + self.vertexFlags = np.array([1 for i in range(len(vert_bottom))]+[2 for i in range(len(vert_top))]) + self.facetFlags = np.array([1, 2]+[3 for i in range(len(self.facets)-2)]) + self.boundaryTags = {'z-': 1, 'z+': 2, 'cylinder': 3} + self.regions = np.array([self.coords]) + self.regionFlags = np.array([1]) + self.volumes = [[[i for i in range(len(self.facets))]]] + self.BC = {'z-': self.BC_class(shape=self, name='z-'), + 'z+': self.BC_class(shape=self, name='z+'), + 'cylinder': self.BC_class(shape=self, name='cylinder')} + self.BC_list = [self.BC['z-'], self.BC['z+'], self.BC['cylinder']] + class Circle(Shape): """ @@ -924,10 +996,10 @@ def __init__(self, domain, barycenter=None, vertices=None, self.boundaryTags = boundaryTags self.vertices = np.array(vertices) self.vertexFlags = np.array(vertexFlags) - if segments: + if segments is not None: self.segments = np.array(segments) self.segmentFlags = np.array(segmentFlags) - if facets: + if facets is not None: self.facets = np.array(facets) self.facetFlags = np.array(facetFlags) if holes is not None: @@ -972,7 +1044,7 @@ def __init__(self, domain, filename): self.vertices, self.facets, self.facetnormals = getInfoFromSTL(self.filename) self.facetFlags = np.ones(len(self.facets)) self.vertexFlags = np.ones(len(self.vertices)) - self.volumes = np.array([[[i for i in range(len(self.facets))]]]) + self.volumes = [[[i for i in range(len(self.facets))]]] self.boundaryTags = {'stl': 1} self.BC = {'stl': self.BC_class(shape=self, name='stl')} self.BC_list = [self.BC['stl']] @@ -1242,12 +1314,12 @@ def _assembleGeometry(domain, BC_class): start_rflag = 0 domain.bc += shape.BC_list # making copies of shape properties before operations/modifications - vertices = shape.vertices.copy() - vertexFlags = shape.vertexFlags.copy() + vertices = copy.deepcopy(shape.vertices) + vertexFlags = copy.deepcopy(shape.vertexFlags) if shape.segments is not None: - segments = shape.segments.copy() + segments = copy.deepcopy(shape.segments) if shape.facets is not None: - facets = shape.facets.copy() + facets = copy.deepcopy(shape.facets) # deleting duplicate vertices and updating segment/facets accordingly del_v = 0 for i_s, vertex in enumerate(shape.vertices): @@ -1281,7 +1353,10 @@ def _assembleGeometry(domain, BC_class): domain.regions += (shape.regions).tolist() domain.regionFlags += (shape.regionFlags+start_rflag).tolist() if shape.facets is not None: - facets = deepcopy(shape.facets.tolist()) + if type(facets) is np.ndarray: + facets = facets.tolist() + else: + facets = copy.deepcopy(shape.facets) for i, facet in enumerate(facets): for j, subf in enumerate(facet): for k, v_nb in enumerate(subf): @@ -1355,7 +1430,11 @@ def _assembleGeometry(domain, BC_class): if shape.holes_ind is not None: domain.holes_ind += (np.array(shape.holes_ind)+shape.start_volume).tolist() if shape.volumes is not None: - volumes = (shape.volumes+shape.start_facet).tolist() + volumes = copy.deepcopy(shape.volumes) + for volume in volumes: + for subvolume in volume: + for i in range(len(subvolume)): + subvolume[i] += shape.start_facet volumes_to_remove = [] for i, volume in enumerate(volumes): # add holes to volumes @@ -1365,7 +1444,10 @@ def _assembleGeometry(domain, BC_class): child_facets = [] # facets in volumes for child_vol in child.volumes: # don't need holes of child volumes, only outer shells: - child_vols += [(child_vol[0]+child.start_facet).tolist()] + child_vol_copy = copy.deepcopy(child_vol[0]) + for i in range(len(child_vol_copy)): + child_vol_copy[i] += child.start_facet + child_vols += [child_vol_copy] if len(child_vols) > 1: # merge volumes that share a facet inter = np.intersect1d(*child_vols) @@ -1403,7 +1485,6 @@ def _generateMesh(domain): # --------------------------- # # ----- MESH GENERATION ----- # # --------------------------- # - comm = Comm.get() mesh = domain.MeshOptions if mesh.outputFiles['poly'] is True: domain.writePoly(mesh.outputFiles_name) diff --git a/proteus/mbd/ChMoorings.h b/proteus/mbd/ChMoorings.h new file mode 100644 index 0000000000..56999806d5 --- /dev/null +++ b/proteus/mbd/ChMoorings.h @@ -0,0 +1,1073 @@ +//#pragma once + +#define _USE_MATH_DEFINES + +#include +#include +#include +#include "chrono/physics/ChSystem.h" +#include "chrono/physics/ChSystemSMC.h" +#include "chrono/physics/ChLoadContainer.h" +#include "chrono/physics/ChBodyEasy.h" +#include "chrono_fea/ChElementBeamANCF.h" +#include "chrono_fea/ChElementCableANCF.h" +#include "chrono_fea/ChElementBeamEuler.h" +#include "chrono_fea/ChBeamSection.h" +#include "chrono_fea/ChMesh.h" +#include "chrono_fea/ChLinkPointPoint.h" +#include "chrono_fea/ChLinkPointFrame.h" +#include "chrono_fea/ChLinkDirFrame.h" +#include "chrono_fea/ChLoadsBeam.h" +#include "chrono_fea/ChContactSurfaceNodeCloud.h" +#include "chrono/timestepper/ChTimestepper.h" +#include "chrono/solver/ChSolverMINRES.h" +#include "chrono/core/ChTransform.h" + + +//using namespace std; +using namespace chrono; +using namespace chrono::fea; + + + +class cppCable { +public: + ChSystemSMC& system; // global system + std::shared_ptr mesh; // mesh + int nb_elems; // number of nodes along cable + double Cd_axial; // drag coeff in axial direction + double Cd_normal; // drag coeff in normal direction + double Cm_axial; // added mass coeff in axial direction + double Cm_normal; // added mass coeff in normal direction + std::vector> mvecs; // vectors (nodes coordinates) + std::vector> mvecs_middle; + std::vector> mdirs; // vectors (nodes coordinates) + double d, rho, E, length; // diameter, density, Young's modulus, length of cable + double A0; // unstretched diameter + double L0 = 0; // initial length along cable + double Iyy; + int nb_nodes; + std::string beam_type; + std::vector> nodes; // array nodes coordinates and direction + std::vector> elems; // array of elements */ + std::vector> elemsCableANCF; // array of elements */ + std::vector> elemsBeamEuler; // array of elements */ + std::vector> nodesRot; // array nodes coordinates and direction + std::shared_ptr mmaterial_cable; // cable material + std::vector> elems_cable; // array of elements */ + std::shared_ptr msection_cable; // cable material + std::shared_ptr msection_advanced; // cable material + std::vector elems_length; // array of elements + std::vector> fluid_velocity; + std::vector> fluid_acceleration; + std::vector fluid_density; + std::vector nodes_density; // density of (cable-fluid) at nodes + cppCable(ChSystemSMC& system, std::shared_ptr mesh, double length, + int nb_elems, double d, double rho, double E, double L0, std::string beam_type); // constructor + void setFluidVelocityAtNodes(std::vector> vel); + void setFluidAccelerationAtNodes(std::vector> acc); + void setFluidDensityAtNodes(std::vector vof); + std::vector>> getNodalPositions(); + std::vector> forces_drag; + std::vector> forces_addedmass; + std::vector> elems_loads_distributed; + std::vector>> elems_loads_volumetric; + std::vector> elems_loads; + void buildVectors(); // builds location vectors for the nodes + void buildNodes(); // builds the nodes for the mesh + void buildNodesBeamANCF(); // builds the nodes for the mesh + void buildNodesBeamEuler(); // builds the nodes for the mesh + void buildNodesCableANCF(); // builds the nodes for the mesh + void buildMaterials(); // builds the material to use for elements + void buildMaterialsBeamANCF(); // builds the material to use for elements + void buildMaterialsCableANCF(); // builds the material to use for elements + void buildMaterialsBeamEuler(); + void buildElements(); // builds the elements for the mesh + void buildElementsBeamANCF(); // builds the elements for the mesh + void buildElementsBeamEuler(); // builds the elements for the mesh + void buildElementsCableANCF(); // builds the elements for the mesh + void buildMesh(); // builds the mesh + void buildMeshBeamANCF(); // builds the mesh + void buildMeshBeamEuler(); // builds the mesh + void buildMeshCableANCF(); // builds the mesh + void setDragForce(); // calculates the drag force per nodes + void setAddedMassForce(); // calculates the added mass force per nodes + void applyForces(); + void addNodestoContactCloud(std::shared_ptr cloud); + void setDragCoefficients(double axial, double normal); + void setAddedMassCoefficients(double axial, double normal); + void setIyy(double Iyy_in); +}; + +class cppMultiSegmentedCable { +public: + ChSystemSMC& system; // global system + std::string beam_type; + std::shared_ptr mysurfmaterial; + std::shared_ptr mesh; // mesh + std::shared_ptr fairleadd; + std::shared_ptr fairlead2; + std::vector nb_nodes; // number of nodes along cable + std::vector nb_elems; // number of nodes along cable + std::vector> mvecs; // vectors (nodes coordinates) + std::vector> cables; + std::shared_ptr contact_material; // mesh + std::vector d; + std::vector rho; + std::vector E; + std::vector length; // diameter, density, Young's modulus, length of cable + std::vector> nodes; // array nodes coordinates and direction + std::vector> nodesRot; // array nodes coordinates and direction + std::vector> fluid_velocity; + std::vector> fluid_acceleration; + std::vector fluid_density; + std::vector> elems; // array of elements */ + std::vector> elemsCableANCF; // array of elements */ + std::vector> elemsBeamEuler; // array of elements */ + std::shared_ptr constraint_front; + std::shared_ptr constraint_back; + std::vector> forces_drag; + std::vector> forces_addedmass; + std::shared_ptr body_back; + std::shared_ptr body_front; + int nb_nodes_tot; + bool nodes_built; + cppMultiSegmentedCable(ChSystemSMC& system, std::shared_ptr mesh, std::vector length, + std::vector nb_nodes, std::vector d, std::vector rho, std::vector E, std::string beam_type); + void setFluidVelocityAtNodes(std::vector> vel); + void setFluidAccelerationAtNodes(std::vector> vel); + void setFluidDensityAtNodes(std::vector dens); + void updateDragForces(); + void updateAddedMassForces(); + void applyForces(); + std::vector>> getNodalPositions(); + void buildNodes(); + void buildCable(); // builds the multi-segmented cable + void getForceFairlead(); + + void attachBackNodeToBody(std::shared_ptr body); + void attachFrontNodeToBody(std::shared_ptr body); + void setContactMaterial(std::shared_ptr material); + void buildNodesCloud(); + ChVector<> getTensionElement(int i); +}; + +class cppMesh { +public: + ChSystemSMC& system; + std::shared_ptr mesh; + cppMesh(ChSystemSMC& system, std::shared_ptr mesh); + void SetAutomaticGravity(bool val); +}; + +cppMesh::cppMesh(ChSystemSMC& system, std::shared_ptr mesh) : + system(system), + mesh(mesh) +{ + system.Add(mesh); +}; + +void cppMesh::SetAutomaticGravity(bool val) { + mesh->SetAutomaticGravity(val); +}; + + + +cppMultiSegmentedCable::cppMultiSegmentedCable( + ChSystemSMC& system, + std::shared_ptr mesh, + std::vector length, + std::vector nb_elems, + std::vector d, + std::vector rho, + std::vector E, + std::string beam_type = "CableANCF") : + system(system), + mesh(mesh), + length(length), + nb_elems(nb_elems), + d(d), + rho(rho), + E(E), + beam_type(beam_type) +{ + nodes_built = false; + std::shared_ptr segment; + double L0 = 0; + for (int i = 0; i < length.size(); ++i) { + segment = std::make_shared(system, mesh, length[i], nb_elems[i], d[i], rho[i], E[i], L0, beam_type); + cables.push_back(segment); + L0 = L0 + length[i]; + } +} + +void cppMultiSegmentedCable::buildNodes() { + nodes.clear(); + nodesRot.clear(); + for (int i = 0; i < cables.size(); ++i) { + if (beam_type == "BeamEuler") { + cables[i]->buildNodes(); + nodesRot.insert(nodesRot.end(), cables[i]->nodesRot.begin(), cables[i]->nodesRot.end()); + } + else { + cables[i]->buildNodes(); + nodes.insert(nodes.end(), cables[i]->nodes.begin(), cables[i]->nodes.end()); + } + nodes_built = true; + if (beam_type == "BeamEuler") { + nb_nodes_tot = nodesRot.size(); + } + else { + nb_nodes_tot = nodes.size(); + } + } + } + +void cppMultiSegmentedCable::buildCable() { + /* builds all cable segments and updates their link + (no duplicate node added to mesh) */ + if (nodes_built == false) { + nodes.clear(); + nodesRot.clear(); + } + elems.clear(); + for (int i = 0; i < cables.size(); ++i) { + cables[i]->buildMaterials(); + if (nodes_built == false) { + cables[i]->buildNodes(); + if (beam_type == "BeamEuler") { + nodesRot.insert(nodesRot.end(), cables[i]->nodesRot.begin(), cables[i]->nodesRot.end()); + } + else { + nodes.insert(nodes.end(), cables[i]->nodes.begin(), cables[i]->nodes.end()); + } + } + cables[i]->buildElements(); + if (beam_type == "BeamANCF") { + elems.insert(elems.end(), cables[i]->elems.begin(), cables[i]->elems.end()); + } + else if (beam_type == "CableANCF") { + elemsCableANCF.insert(elemsCableANCF.end(), cables[i]->elemsCableANCF.begin(), cables[i]->elemsCableANCF.end()); + } + else if (beam_type == "BeamEuler") { + elemsBeamEuler.insert(elemsBeamEuler.end(), cables[i]->elemsBeamEuler.begin(), cables[i]->elemsBeamEuler.end()); + } + cables[i]->buildMesh(); + if (i>0) { + auto con1 = std::make_shared(); + if (beam_type == "BeamEuler") { + //auto nodeA = cables[i]->nodesRot.front(); + //auto nodeB = cables[i-1]->nodesRot.back(); + //con1->Initialize(nodeA, nodeB); + //system.Add(con1); + } + else { + auto nodeA = cables[i]->nodes.front(); + auto nodeB = cables[i-1]->nodes.back(); + con1->Initialize(nodeA, nodeB); + system.Add(con1); + } + } + } + buildNodesCloud(); + fluid_velocity.clear(); + fluid_acceleration.clear(); + fluid_density.clear(); + if (beam_type == "BeamEuler") { + nb_nodes_tot = nodesRot.size(); + } + else { + nb_nodes_tot = nodes.size(); + } + for (int i = 0; i < nb_nodes_tot; ++i) { + fluid_velocity.push_back(ChVector<>(0.,0.,0.)); + fluid_acceleration.push_back(ChVector<>(0.,0.,0.)); + fluid_density.push_back(0.); + } + setFluidVelocityAtNodes(fluid_velocity); + setFluidAccelerationAtNodes(fluid_acceleration); + setFluidDensityAtNodes(fluid_density); +} + +void cppMultiSegmentedCable::setFluidAccelerationAtNodes(std::vector> acc) { + fluid_acceleration = acc; + int node_nb = 0; + for (int i = 0; i < cables.size(); ++i) { + std::vector> fluid_acc(fluid_acceleration.begin() + node_nb, fluid_acceleration.begin() + node_nb + cables[i]->nodes.size()); + cables[i]->setFluidAccelerationAtNodes(fluid_acc); + node_nb += cables[i]->nodes.size(); + } +} + +void cppMultiSegmentedCable::setFluidVelocityAtNodes(std::vector> vel) { + fluid_velocity = vel; + int node_nb = 0; + for (int i = 0; i < cables.size(); ++i) { + std::vector> fluid_vel(fluid_velocity.begin() + node_nb, fluid_velocity.begin() + node_nb + cables[i]->nb_nodes); + cables[i]->setFluidVelocityAtNodes(fluid_vel); + node_nb += cables[i]->nb_nodes; + } +} + +void cppMultiSegmentedCable::setFluidDensityAtNodes(std::vector dens) { + fluid_density = dens; + int node_nb = 0; + for (int i = 0; i < cables.size(); ++i) { + std::vector fluid_dens(fluid_density.begin() + node_nb, fluid_density.begin() + node_nb + cables[i]->nb_nodes); + cables[i]->setFluidDensityAtNodes(fluid_dens); + node_nb += cables[i]->nb_nodes; + } +} + + +void cppMultiSegmentedCable::updateDragForces() { + forces_drag.clear(); + for (int i = 0; i < cables.size(); ++i) { + cables[i]->setDragForce(); + forces_drag.insert(forces_drag.end(), cables[i]->forces_drag.begin(), cables[i]->forces_drag.end()); + }; +} + +void cppMultiSegmentedCable::updateAddedMassForces() { + forces_addedmass.clear(); + for (int i = 0; i < cables.size(); ++i) { + cables[i]->setAddedMassForce(); + forces_addedmass.insert(forces_addedmass.end(), cables[i]->forces_addedmass.begin(), cables[i]->forces_addedmass.end()); + }; +} + +void cppMultiSegmentedCable::applyForces() { + for (int i = 0; i < cables.size(); ++i) { + cables[i]->applyForces(); + }; +} + +ChVector<> cppMultiSegmentedCable::getTensionElement(int i) { + + auto mat = ChMatrix<>(); + auto force = ChVector<>(); + auto torque = ChVector<>(); + double eta = 0.; + if (beam_type == "BeamANCF") { + elems[i]->EvaluateSectionForceTorque(eta, + mat, + force, + torque); + + } + else if (beam_type == "CableANCF") { + elemsCableANCF[i]->EvaluateSectionForceTorque(eta, + mat, + force, + torque); + elemsCableANCF[i]->EvaluateSectionStrain(eta, mat, force); + } + else if (beam_type == "BeamEuler") { + elemsBeamEuler[i]->EvaluateSectionStrain(eta, mat, force); + /* elemsBeamEuler[i]->EvaluateSectionForceTorque(eta, */ + /* mat, */ + /* force, */ + /* torque); */ + } + return force; +} + +std::vector>> cppMultiSegmentedCable::getNodalPositions() { + std::vector>> nodal_positions; + for (int i = 0; i < nodes.size(); ++i) { + auto pos = nodes[i]->GetPos(); + double x = pos.x(); + double y = pos.y(); + double z = pos.z(); + auto nodal_position = std::make_shared>(x, y, z); + nodal_positions.push_back(nodal_position); + } + return nodal_positions; +} + +void cppMultiSegmentedCable::attachBackNodeToBody(std::shared_ptr body) { + auto constraint = std::make_shared(); + if (beam_type == "BeamEuler") { + //constraint->Initialize(nodesRot.back(), body); + /* system.Add(constraint); */ + /* constraint_back = constraint; */ + /* body_back = body; */ + } + else { + constraint->Initialize(nodes.back(), body); + system.Add(constraint); + constraint_back = constraint; + body_back = body; + } +}; + +void cppMultiSegmentedCable::attachFrontNodeToBody(std::shared_ptr body) { + auto constraint = std::make_shared(); + if (beam_type == "BeamEuler") { + //constraint->Initialize(nodesRot.front(), body); + /* system.Add(constraint); */ + /* constraint_front = constraint; */ + /* body_front = body; */ + } + else { + constraint->Initialize(nodes.front(), body); + system.Add(constraint); + constraint_front = constraint; + body_front = body; + } +}; + +void cppMultiSegmentedCable::setContactMaterial(std::shared_ptr material) { + contact_material = material; +}; + +void cppMultiSegmentedCable::buildNodesCloud() { + if (contact_material) { + auto contact_cloud = std::make_shared(); + mesh->AddContactSurface(contact_cloud); + // Use DEM surface material properties + contact_cloud->SetMaterialSurface(contact_material); + // add cable nodes to cloud + for (int i = 0; i < cables.size(); ++i) { + cables[i]->addNodestoContactCloud(contact_cloud); + } + } +}; + + +cppCable::cppCable( + ChSystemSMC& system, // system in which the cable belong + std::shared_ptr mesh, // mesh of the cable + double length, // length of cable + int nb_elems, // number of nodes along cable + double d, // diameter of cable + double rho, // density of cable (kg/m3) + double E, // Young's modulus + double L0 = 0, + std::string beam_type = "CableANCF" +) : + system(system), + mesh(mesh), + length(length), + nb_elems(nb_elems), + d(d), + rho(rho), + E(E), + L0(L0), + beam_type(beam_type) +{ + Cd_axial = 1.15; // studless chain + Cd_normal = 1.4; // studless chain + Cm_axial = 0.5; //studless chain + Cm_normal = 1.; // studless chain + A0 = d*d/4*M_PI; + Iyy = 1e-12; + // TO CHANGE !!! + //buildMaterials(); + //buildVectors(); + //buildCable(nb_nodes); + //applyConstraints(); + //double g = { length }; +} + +void cppCable::buildMaterials() { + if (beam_type == "BeamANCF") { + buildMaterialsBeamANCF(); + } + else if (beam_type == "CableANCF") { + buildMaterialsCableANCF(); + } + else if (beam_type == "BeamEuler") { + buildMaterialsBeamEuler(); + } +} +void cppCable::buildMaterialsBeamANCF() { + // make material characteristics of cable + auto nu = ChVector<>(0.3, 0.3, 0.3); + double E2 = E / nu.y()*nu.x(); + auto EE = ChVector<>(E, E2, E2); + double G = EE.z() / (2 * (1 + nu.z())); + /* double G2 = G; */ + double G2 = 1e-6; + auto GG = ChVector<>(G, G2, G2); + double k_rect = 10 * (1 + nu.x()) / (12 + 11 * nu.x()); // rectangular cross-section + double k_circ = 6 * (1 + nu.x()) / (7 + 6 * nu.x()); + //mmaterial_cable = std::make_shared(rho, EE, nu, GG, 0.84, 0.84); + mmaterial_cable = std::make_shared(rho, EE, nu, GG, k_circ, k_circ); +} + +void cppCable::buildMaterialsCableANCF() { + msection_cable = std::make_shared(); + msection_cable->SetDiameter(d); + msection_cable->SetYoungModulus(E); + msection_cable->SetDensity(rho); + msection_cable->SetI(Iyy); +} + +void cppCable::buildMaterialsBeamEuler() { + msection_advanced = std::make_shared(); + msection_advanced->SetYoungModulus(E); + msection_advanced->SetGshearModulus(1e-6); + msection_advanced->SetDensity(rho); + msection_advanced->SetAsCircularSection(d); + msection_advanced->SetIyy(Iyy); + msection_advanced->SetIzz(Iyy); +} + +void cppCable::setIyy(double Iyy_in) { + Iyy = Iyy_in; +} + +void cppCable::buildNodes() { + if (beam_type == "BeamANCF") { + buildNodesBeamANCF(); + } + else if (beam_type == "CableANCF") { + buildNodesCableANCF(); + } + else if (beam_type == "BeamEuler") { + buildNodesBeamEuler(); + } +} + +void cppCable::buildNodesBeamEuler() { + nodesRot.clear(); + nb_nodes = nb_elems+1; + ChVector<> dir; // direction of node + /* ChQuaternion<> quat; // direction of node */ + std::shared_ptr node; + // first node + dir = (mvecs[1] - mvecs[0]); + dir.Normalize(); + /* quat = Q_from_AngAxis(dir, 0.); */ + node = std::make_shared(ChFrame<>(mvecs[0])); + nodesRot.push_back(node); + // other nodes + for (int i = 1; i < mvecs.size() - 1; ++i) { + dir = mvecs[i + 1] - mvecs[i - 1]; + dir.Normalize(); + /* quat = Q_from_AngAxis(dir, 0.); */ + node = std::make_shared(ChFrame<>( mvecs[i] )); + nodesRot.push_back(node); + } // last node + dir = mvecs[nb_nodes - 1] - mvecs[nb_nodes - 2]; + dir.Normalize(); + /* quat = Q_from_AngAxis(dir, 0.); */ + node = std::make_shared(ChFrame<>(mvecs[mvecs.size() - 1])); + nodesRot.push_back(node); +} + +void cppCable::buildNodesCableANCF() { + nodes.clear(); + nb_nodes = nb_elems+1; + ChVector<> dir; // direction of node + ChVector<> norm1; // normal of node direction + ChVector<> norm2; // normal of node direction and norm1 + ChCoordsys<> coordsys; // coordinate system + ChVector<> ref = ChVector<>(1., 0., 0.); + std::shared_ptr node; + // first node + dir = (mvecs[1] - mvecs[0]); + dir.Normalize(); + node = std::make_shared(mvecs[0], dir, ref); + nodes.push_back(node); + // other nodes + for (int i = 1; i < mvecs.size() - 1; ++i) { + dir = mvecs[i + 1] - mvecs[i - 1]; + dir.Normalize(); + node = std::make_shared(mvecs[i], dir, ref); + nodes.push_back(node); + } // last node + dir = mvecs[nb_nodes - 1] - mvecs[nb_nodes - 2]; + dir.Normalize(); + node = std::make_shared(mvecs[mvecs.size() - 1], dir, ref); + nodes.push_back(node); +} + +void cppCable::buildNodesBeamANCF() { + nodes.clear(); + nb_nodes = 2*nb_elems+1; + ChVector<> dir; // direction of node + ChVector<> norm1; // normal of node direction + ChVector<> norm2; // normal of node direction and norm1 + ChCoordsys<> coordsys; // coordinate system + ChVector<> ref = ChVector<>(1., 0., 0.); + std::shared_ptr node; + // first node + dir = (mvecs[1] - mvecs[0]); + dir.Normalize(); + //plane = dir.x()*(x - mvecs[0].x()) + dir.y()*(y - mvecs[0].y()) + dir.z()*(z - mvecs[0].z()); + if (dir.x() == 1) { + ref = ChVector<>(0., 0., -1.); + ref.Normalize(); + } + else if (dir.x() == -1) { + ref = ChVector<>(0., 0., 1.); + ref.Normalize(); + } + else { + ref = ChVector<>(1., 0., 0.); + } + norm1 = dir % ref; + norm1.Normalize(); + norm2 = dir % norm1; + norm2.Normalize(); + //norm2 = dir % norm1; + node = std::make_shared(mvecs[0], norm1, norm2); + nodes.push_back(node); + // other nodes + for (int i = 1; i < mvecs.size() - 1; ++i) { + dir = mvecs[i + 1] - mvecs[i - 1]; + dir.Normalize(); + if (dir.x() == 1) { + ref = ChVector<>(0., 0., -1.); + ref.Normalize(); + } + else if (dir.x() == -1) { + ref = ChVector<>(0., 0., 1.); + ref.Normalize(); + } + else { + ref = ChVector<>(1., 0., 0.); + } + norm1 = dir % ref; + norm1.Normalize(); + norm2 = dir % norm1; + norm2.Normalize(); + node = std::make_shared(mvecs[i], norm1, norm2); + nodes.push_back(node); + } // last node + dir = mvecs[nb_nodes - 1] - mvecs[nb_nodes - 2]; + dir.Normalize(); + if (dir.x() == 1) { + ref = ChVector<>(0., 0., -1.); + ref.Normalize(); + } + else if (dir.x() == -1) { + ref = ChVector<>(0., 0., 1.); + ref.Normalize(); + } + else { + ref = ChVector<>(1., 0., 0.); + } + norm1 = dir % ref; + norm1.Normalize(); + norm2 = dir % norm1; + norm2.Normalize(); + node = std::make_shared(mvecs[mvecs.size() - 1], norm1, norm2); + nodes.push_back(node); +} + +void cppCable::buildElements() { + if (beam_type == "BeamANCF") { + buildElementsBeamANCF(); + } + else if (beam_type == "CableANCF") { + buildElementsCableANCF(); + } + else if (beam_type == "BeamEuler") { + buildElementsBeamEuler(); + } +} + +void cppCable::buildElementsCableANCF() { + auto loadcontainer = std::make_shared(); + system.Add(loadcontainer); + // build elements + elemsCableANCF.clear(); + elems_loads_distributed.clear(); + elems_loads.clear(); + for (int i = 0; i < nodes.size() - 1; ++i) { + auto element = std::make_shared(); + auto load_distributed = std::make_shared(element); + auto load = std::make_shared(element); + auto load_volumetric = std::make_shared>(element); + loadcontainer->Add(load_distributed); + loadcontainer->Add(load); + loadcontainer->Add(load_volumetric); + elemsCableANCF.push_back(element); + elems_loads_distributed.push_back(load_distributed); + elems_loads.push_back(load); + elems_loads_volumetric.push_back(load_volumetric); + element->SetNodes(nodes[i], nodes[i + 1]); + element->SetSection(msection_cable); + } + } + +void cppCable::buildElementsBeamEuler() { + auto loadcontainer = std::make_shared(); + system.Add(loadcontainer); + // build elements + elemsBeamEuler.clear(); + elems_loads_distributed.clear(); + elems_loads.clear(); + for (int i = 0; i < nodesRot.size() - 1; ++i) { + auto element = std::make_shared(); + auto load_distributed = std::make_shared(element); + auto load = std::make_shared(element); + loadcontainer->Add(load_distributed); + loadcontainer->Add(load); + elemsBeamEuler.push_back(element); + elems_loads_distributed.push_back(load_distributed); + elems_loads.push_back(load); + element->SetNodes(nodesRot[i], nodesRot[i + 1]); + element->SetSection(msection_advanced); + } +} + +void cppCable::buildElementsBeamANCF() { + auto loadcontainer = std::make_shared(); + system.Add(loadcontainer); + // build elements + elems.clear(); + elems_loads_distributed.clear(); + elems_loads.clear(); + for (int i = 1; i < nodes.size() - 1; ++i) { + if (i % 2 != 0) { + auto element = std::make_shared(); + auto load_distributed = std::make_shared(element); + auto load = std::make_shared(element); + loadcontainer->Add(load_distributed); + loadcontainer->Add(load); + elems.push_back(element); + elems_loads_distributed.push_back(load_distributed); + elems_loads.push_back(load); + element->SetNodes(nodes[i - 1], nodes[i + 1], nodes[i]); + double elem_length = (nodes[i]->GetPos()-nodes[i-1]->GetPos()).Length()+(nodes[i+1]->GetPos()-nodes[i]->GetPos()).Length(); + double d2 = sqrt(d*d-(d*d-CH_C_PI*d*d/4.)); + d2 = sqrt(d2*d2/2.); //dividing by 2 because cable thickness seems to be only half of the total thickness in chrono + element->SetDimensions(elem_length, d2, d2); + element->SetMaterial(mmaterial_cable); + element->SetAlphaDamp(0.0004); + element->SetGravityOn(true); + element->SetStrainFormulation(ChElementBeamANCF::StrainFormulation::CMNoPoisson); + element->SetupInitial(&system); + } + } +} + +void cppCable::buildMesh() { + if (beam_type == "BeamANCF") { + buildMeshBeamANCF(); + } + else if (beam_type == "CableANCF") { + buildMeshCableANCF(); + } + else if (beam_type == "BeamEuler") { + buildMeshBeamEuler(); + } +} + +void cppCable::buildMeshBeamEuler() { + // build the mesh (nodes and elements) + auto node = elemsBeamEuler[0]->GetNodeA(); + mesh->AddNode(node); + for (int i = 0; i < elemsBeamEuler.size(); ++i) { + auto node = elemsBeamEuler[i]->GetNodeB(); + mesh->AddNode(node); + mesh->AddElement(elemsBeamEuler[i]); + } +} + +void cppCable::buildMeshCableANCF() { + // build the mesh (nodes and elements) + auto node = elemsCableANCF[0]->GetNodeA(); + mesh->AddNode(node); + for (int i = 0; i < elemsCableANCF.size(); ++i) { + auto node = elemsCableANCF[i]->GetNodeB(); + mesh->AddNode(node); + mesh->AddElement(elemsCableANCF[i]); + } +} +void cppCable::buildMeshBeamANCF() { + // build the mesh (nodes and elements) + auto node = elems[0]->GetNodeA(); + mesh->AddNode(node); + for (int i = 0; i < elems.size(); ++i) { + auto node = elems[i]->GetNodeB(); + mesh->AddNode(node); + node = elems[i]->GetNodeC(); + mesh->AddNode(node); + mesh->AddElement(elems[i]); + } +} + +void cppCable::setFluidAccelerationAtNodes(std::vector> acc) { + fluid_acceleration = acc; +} + +void cppCable::setFluidVelocityAtNodes(std::vector> vel) { + fluid_velocity = vel; +} + +void cppCable::setFluidDensityAtNodes(std::vector dens) { + fluid_density = dens; +} + +void cppCable::setDragCoefficients(double axial, double normal) { + Cd_axial = axial; + Cd_normal = normal; +} + + +void cppCable::setAddedMassCoefficients(double axial, double normal) { + Cm_axial = axial; + Cm_normal = normal; +} + +void cppCable::setDragForce() { + /* + * setFluidVelocityAtNodes and setFluidDensityAtNodes + * must be called before this function + */ + ChVector<> u_ch; // velocity from chrono + ChVector<> u_prot; // velocity from proteus + ChVector<> u_rel; // relative velocity of node with surrounding fluid + ChVector<> t_dir; // tangent at node + ChVector<> Fd_a; // axial (tangential) drag force + ChVector<> Fd_n; // normal(transversal) drag force + ChVector<> Fd; // total drag force + ChVector<> Va; + ChVector<> Vn; + double rho_f; + // clear current drag forces + forces_drag.clear(); + double length_elem = length / (nb_nodes - 1); + for (int i = 0; i < nodes.size(); ++i) { + u_ch = nodes[i]->GetPos_dt(); + // get velocity u_prot from proteus // TO CHANGE !! + double ux_prot = fluid_velocity[i][0]; + double uy_prot = fluid_velocity[i][1]; + double uz_prot = fluid_velocity[i][2]; + u_prot = ChVector<>(ux_prot, uy_prot, uz_prot); + u_rel = u_prot - u_ch; + // CAREFUL HERE: ChBeamElementANCF, GetD() does not give direction but normal + t_dir = nodes[i]->GetD() % nodes[i]->GetDD(); + // transverse drag coefficient + /* double C_dn = 0.; // transversal drag coeff */ + /* double C_dt = 0.; // tangential drag coeff */ + rho_f = fluid_density[i]; + Va = u_rel^t_dir*t_dir; + Vn = u_rel-Va; + Fd_a = 0.5*rho_f*Cd_axial*d*Va.Length()*Va;//(force per unit length) + Fd_n = 0.5*rho_f*Cd_normal*M_PI*d*Vn.Length()*Vn;//(force per unit length) + Fd = Fd_a + Fd_n; + forces_drag.push_back(Fd); + if (i == 0) { + GetLog() << "u_rel\n"; + GetLog() << u_rel; + GetLog() << "Direction\n"; + GetLog() << t_dir; + GetLog() << "Vn\n"; + GetLog() << Vn; + GetLog() << "Va\n"; + GetLog() << Va; + GetLog() << "Fd\n"; + GetLog() << Fd; + } + } +} + + +void cppCable::setAddedMassForce() { + /* + * setFluidVelocityAtNodes and setFluidDensityAtNodes + * must be called before this function + */ + ChVector<> a_ch; // acceleration from chrono + ChVector<> a_prot; // acceleration from proteus + ChVector<> a_rel; // relative acceleration of node with surrounding fluid + ChVector<> t_dir; // tangent at node + ChVector<> Fm_a; // axial (tangential) added mass force + ChVector<> Fm_n; // normal(transversal) added mass force + ChVector<> Fm; // total added mass force + ChVector<> Va; + ChVector<> Vn; + double rho_f; + // clear current drag forces + forces_drag.clear(); + double length_elem = length / (nb_nodes - 1); + for (int i = 0; i < nodes.size(); ++i) { + a_ch = nodes[i]->GetPos_dtdt(); + // get velocity u_prot from proteus // TO CHANGE !! + double ax_prot = fluid_acceleration[i][0]; + double ay_prot = fluid_acceleration[i][1]; + double az_prot = fluid_acceleration[i][2]; + a_prot = ChVector<>(ax_prot, ay_prot, az_prot); + a_rel = a_prot - a_ch; + // CAREFUL HERE: ChBeamElementANCF, GetD() does not give direction but normal + t_dir = nodes[i]->GetD() % nodes[i]->GetDD(); + // transverse drag coefficient + /* double C_dn = 0.; // transversal drag coeff */ + /* double C_dt = 0.; // tangential drag coeff */ + rho_f = fluid_density[i]; + Va = a_rel^t_dir*t_dir; + Vn = a_rel-Va; + Fm_a = rho_f*Cm_axial*M_PI*d*d/4.*Va;//(force per unit length) + Fm_n = rho_f*Cm_normal*M_PI*d*d/4.*Vn;//(force per unit length) + Fm = Fm_a + Fm_n; + forces_addedmass.push_back(Fm); + } +} + +void cppCable::applyForces() { + ChVector<> F_drag; // drag force per unit length + ChVector<> F_buoyancy; // buoyancy force per unit length + ChVector<> F_buoyancy2; // buoyancy force + ChVector<> F_total; // total force per unit length + ChVector<> F_addedmass; // buoyancy force per unit length + double rho_f; // density of fluid around cable element + double mass_f; // mass of fluid displaced by cable element + /* for (int i = 1; i < nodes.size()-1; ++i) { */ + /* if (i % 2 != 0) { */ + /* F_drag = (forces_drag[i-1]+forces_drag[i]+forces_drag[i+1])/3; */ + /* F_addedmass = (forces_addedmass[i-1]+forces_addedmass[i]+forces_addedmass[i+1])/3; */ + /* F_total = F_drag+F_addedmass;//+F_buoyancy;//+F_fluid */ + /* elems_loads_distributed[i/2]->loader.SetForcePerUnit(F_total); */ + /* // buoyancy */ + /* rho_f = -(fluid_density[i-1]+fluid_density[i]+fluid_density[i+1])/3.; */ + /* mass_f = rho_f*A0*elems[i/2]->GetRestLength(); */ + /* F_buoyancy = -mass_f*system.Get_G_acc(); */ + /* elems_loads[i/2]->loader.SetForce(F_buoyancy); */ + /* } */ + /* } */ + double lengths = 0; + for (int i = 1; i < nodes.size()-1; ++i) { + F_drag = (forces_drag[i]+forces_drag[i+1])/2; + F_addedmass = (forces_addedmass[i]+forces_addedmass[i+1])/2; + F_total = F_drag+F_addedmass;//+F_buoyancy;//+F_fluid + elems_loads_distributed[i]->loader.SetForcePerUnit(F_total); + lengths = lengths + elemsCableANCF[i]->GetRestLength(); + // buoyancy + elems_loads_volumetric[i]->loader.Set_G_acc(system.Get_G_acc()*(rho-fluid_density[i])/rho-system.Get_G_acc()); // remove gravity because it is there on top of it + /* if (i == 50) { */ + /* GetLog() << "buoyancy\n"; */ + /* GetLog() << system.Get_G_acc()*(rho-fluid_density[i])/rho-system.Get_G_acc(); */ + /* GetLog() << "drag\n"; */ + /* GetLog() << F_drag; */ + /* GetLog() << "addedmass\n"; */ + /* GetLog() << F_addedmass; */ + /* } */ + } + }; + +void cppCable::addNodestoContactCloud(std::shared_ptr cloud) { + for (int i = 0; i < nb_nodes; ++i) { + if (beam_type == "BeamEuler") { + cloud->AddNode(nodesRot[i], d); + } + else { + cloud->AddNode(nodes[i], d); + } + } +}; + +cppMultiSegmentedCable * newMoorings( + ChSystemSMC& system, + std::shared_ptr mesh, + std::vector length, + std::vector nb_elems, + std::vector d, + std::vector rho, + std::vector E, + std::string beam_type) +{ + return new cppMultiSegmentedCable(system, mesh, length, nb_elems, d, rho, E, beam_type); +} + +cppMesh * newMesh(ChSystemSMC& system, std::shared_ptr mesh) { + return new cppMesh(system, mesh); +} + + + + + + + +class cppSurfaceBoxNodesCloud { + public: + ChSystemSMC& system; + std::shared_ptr mesh; + ChVector<> position; + ChVector<> dimensions; + std::shared_ptr box; + std::shared_ptr material; + std::shared_ptr contact_cloud; + cppSurfaceBoxNodesCloud(ChSystemSMC& system, + std::shared_ptr mesh, + ChVector<> position, + ChVector<> dimensions); + void setNodesSize(double size); +}; + +cppSurfaceBoxNodesCloud::cppSurfaceBoxNodesCloud(ChSystemSMC& system, + std::shared_ptr mesh, + ChVector<> position, + ChVector<> dimensions) : + system(system), + mesh(mesh), + position(position), + dimensions(dimensions) +{ + // Create a surface material to be shared with some objects + material = std::make_shared(); + material->SetYoungModulus(2e4); + material->SetFriction(0.3f); + material->SetRestitution(0.2f); + material->SetAdhesion(0); + contact_cloud = std::make_shared(); + mesh->AddContactSurface(contact_cloud); + // Must use this to 'populate' the contact surface. + // Use larger point size to match beam section radius + contact_cloud->AddAllNodes(0.01); + + // Use our DEM surface material properties + contact_cloud->SetMaterialSurface(material); + + box = std::make_shared( + dimensions.x(), dimensions.y(), dimensions.z(), // x,y,z size + 1000, // density + true // collide + ); + + system.Add(box); + + box->SetBodyFixed(true); + box->SetPos(position); + + // Use our DEM surface material properties + box->SetMaterialSurface(material); +}; + +void cppSurfaceBoxNodesCloud::setNodesSize(double size) { + contact_cloud->AddAllNodes(size); +} + +cppSurfaceBoxNodesCloud * newSurfaceBoxNodesCloud(ChSystemSMC& system, + std::shared_ptr mesh, + ChVector<> position, + ChVector<> dimensions) { + return new cppSurfaceBoxNodesCloud(system, mesh, position, dimensions); +} + +/* class MyLoaderTriangular : public ChLoaderUdistributed { */ +/* public: */ +/* // Useful: a constructor that also sets ChLoadable */ +/* MyLoaderTriangular(std::shared_ptr mloadable) : ChLoaderUdistributed(mloadable){}; */ +/* // Compute F=F(u) */ +/* // This is the function that you have to implement. It should return the */ +/* // load at U. For Eulero beams, loads are expected as 6-rows vectors, containing */ +/* // a wrench: forceX, forceY, forceZ, torqueX, torqueY, torqueZ. */ +/* virtual void ComputeF( */ +/* const double U, */ +/* ChVectorDynamic<>& F, */ +/* ChVectorDynamic<>* state_x, */ +/* ChVectorDynamic<>* state_w */ +/* ) { */ +/* double Fy_max = 0.005; */ +/* ChVector<> force = -(U-1)/2*F1 + (U+1)/2*F2 */ +/* F.PasteVector(ChVector<>) */ +/* F.PasteVector(ChVector<>(0, (((1 + U) / 2) * Fy_max), 0), 0, 0); // load, force part; hardwired for brevity */ +/* F.PasteVector(ChVector<>(0, 0, 0), 3, 0); // load, torque part; hardwired for brevity */ +/* } */ diff --git a/proteus/mbd/ChRigidBody.h b/proteus/mbd/ChRigidBody.h new file mode 100644 index 0000000000..902c217c98 --- /dev/null +++ b/proteus/mbd/ChRigidBody.h @@ -0,0 +1,442 @@ +#include "chrono/physics/ChSystemSMC.h" +#include "chrono/timestepper/ChTimestepper.h" +#include "chrono/solver/ChSolverMINRES.h" +#include "chrono/core/ChTransform.h" +#include +#include + +using namespace chrono; +using namespace std; + + + +class cppSystem { + public: + ChSystemSMC system; + double* gravity; + double chrono_dt; + std::string directory; + cppSystem(double* gravity); + void step(double proteus_dt, int n_substeps); + void setChTimeStep(double dt); + void recordBodyList(); + void setGravity(double* gravity); + void setDirectory(std::string dir); + void setTimestepperType(std::string tstype, bool verbose); +}; + + +class cppRigidBody { + public: + ChVector<> free_x; + ChVector<> free_r; + ChVector<> pos; + ChVector<> pos_last; + ChVector<> vel; + ChVector<> vel_last; + ChVector<> acc; + ChVector<> acc_last; + ChVector<> angvel; + ChVector<> angvel_last; + ChVector<> angacc; + ChVector<> angacc_last; + ChMatrix33 rotm; + ChMatrix33 rotm_last; + ChQuaternion rotq; + ChQuaternion rotq_last; + ChVector<> F; + ChVector<> F_last; + ChVector<> M; + ChVector<> M_last; + double mass; + double mooring_restlength; + std::shared_ptr spring; + /* ChVector <> inertia; */ + double* inertia; + std::shared_ptr body; + cppSystem* system; + cppRigidBody(cppSystem* system); + ChVector hxyz(double* x, double t); + double hx(double* x, double t); + double hy(double* x, double t); + double hz(double* x, double t); + void prestep(double* force, double* torque); + void poststep(); + void setRotation(double* quat); + void setPosition(double* quat); + void setConstraints(double* free_x, double* free_y); + void setInertiaXX(double* inertia); + void addSpring(double stiffness, + double damping, + double* fairlead, + double* anchor, + double rest_length); + void addPrismaticLinksWithSpring(double* pris1, + double* pris2, + double stiffness, + double damping, + double rest_length); + void setName(std::string name); + void setPrescribedMotionPoly(double coeff1); + void setPrescribedMotionSine(double a, double f); +}; + +cppSystem::cppSystem(double* gravity): +gravity(gravity) +{ + chrono_dt = 0.000001; + system.Set_G_acc(ChVector<>(gravity[0], gravity[1], gravity[2])); + directory = "./"; + // SOLVER OPTIONS + system.SetSolverType(ChSolver::Type::MINRES); // SOLVER_MINRES: good convergence, supports FEA, does not support DVI yet + auto msolver = std::static_pointer_cast(system.GetSolver()); + msolver->SetDiagonalPreconditioning(true); + system.SetSolverWarmStarting(true); // this helps a lot to speedup convergence in this class of problems + system.SetMaxItersSolverSpeed(100); // max iteration for iterative solvers + system.SetMaxItersSolverStab(100); // max iteration for stabilization (iterative solvers) + system.SetTolForce(1e-13); + //system.SetMaxItersSolverSpeed(100); + //system.SetMaxItersSolverStab(100); + //system.SetTolForce(1e-14); // default: 0.001 + //system.SetMaxiter(200); // default: 6. Max constraints to reach tolerance on constraints. + //system.SetTol(1e-10); // default: 0.0002. Tolerance for keeping constraints together. + system.SetTimestepperType(ChTimestepper::Type::EULER_IMPLICIT_LINEARIZED); // used before: ChSystemSMC::INT_EULER_IMPLICIT_LINEARIZED + if (auto mystepper = std::dynamic_pointer_cast(system.GetTimestepper())) { + mystepper->SetAlpha(-0.2); + } + //system.SetTimestepper(std::make_shared()); // default: fast, 1st order +} + +void cppSystem::setTimestepperType(std::string tstype, bool verbose=false) { + if (tstype == "HHT") { + system.SetTimestepperType(ChTimestepper::Type::HHT); + auto mystepper = std::dynamic_pointer_cast(system.GetTimestepper()); + mystepper->SetAlpha(-0.2); + mystepper->SetMaxiters(10); + mystepper->SetAbsTolerances(1e-6); + mystepper->SetMode(ChTimestepperHHT::POSITION); + mystepper->SetScaling(false); + mystepper->SetVerbose(verbose); + mystepper->SetModifiedNewton(false); + } + else if (tstype == "Euler") { + system.SetTimestepperType(ChTimestepper::Type::EULER_IMPLICIT_LINEARIZED); + } + else if (tstype == "Trapezoidal") { + system.SetTimestepperType(ChTimestepper::Type::TRAPEZOIDAL); + } + } + +void cppSystem::setGravity(double* gravity) +{ + system.Set_G_acc(ChVector<>(gravity[0], gravity[1], gravity[2])); +} + +void cppSystem::step(double proteus_dt, int n_substeps=1) +{ + /* std::vector>& bodylist = *system.Get_bodylist(); */ + /* std::shared_ptr bod = bodylist[0]; */ + /* GetLog() << "nan test2" << bod->GetPos() ; */ + double dt2 = proteus_dt/(double)n_substeps; + for (int i = 0; i < n_substeps; ++i) { + /* GetLog() << dt2*i << " " << dt2 << " " << i << " "<< n_substeps << bod->GetPos(); */ + system.DoStepDynamics(dt2); + } +} + +void cppSystem::recordBodyList() { + std::vector>& bodylist = *system.Get_bodylist(); + double t = system.GetChTime(); + if (t == 0) { + for (int i = 0; i bod = bodylist[i]; + fstream myfile; + myfile.open (directory+bod->GetNameString()+".csv", std::ios_base::out); + myfile << "t,x,y,z,e0,e1,e2,e3,ux,uy,uz,ax,ay,az,Fx,Fy,Fz,Mx,My,Mz,\n"; + myfile.close(); + } + } + for (int i = 0; i bod = bodylist[i]; + fstream myfile; + myfile.open (directory+bod->GetNameString()+".csv", std::ios_base::app); + ChVector<> bpos = bod->GetPos(); + ChVector<> bvel = bod->GetPos_dt(); + ChVector<> bacc = bod->GetPos_dtdt(); + ChVector<> bfor = bod->Get_Xforce(); + ChVector<> btor = bod->Get_Xtorque(); + ChQuaternion<> brot = bod->GetRot(); + myfile << t << ","; + myfile << bpos.x() << "," << bpos.y() << "," << bpos.z() << ","; + myfile << brot.e0() << "," << brot.e1() << "," << brot.e2() << "," << brot.e3() << ","; + myfile << bvel.x() << "," << bvel.y() << "," << bvel.z() << ","; + myfile << bacc.x() << "," << bacc.y() << "," << bacc.z() << ","; + myfile << bfor.x() << "," << bfor.y() << "," << bfor.z() << ","; + myfile << btor.x() << "," << btor.y() << "," << btor.z() << ","; + myfile << "\n"; + myfile.close(); + } +} + + +void cppSystem::setChTimeStep(double dt) { + chrono_dt = dt; +}; + +cppRigidBody::cppRigidBody(cppSystem* system): + system(system) +{ + + body = std::make_shared(); + // add body to system + /* system->system.AddBody(body); */ // now added externally in cython + // basic attributes of body + rotm = body->GetA(); + rotm_last = body->GetA(); + pos = body->GetPos(); + pos_last = body->GetPos(); + body->SetMass(mass); +} + + +void cppSystem::setDirectory(std::string dir) { + directory = dir; +} + +ChVector cppRigidBody::hxyz(double* x, double t) +{ + /* rotm = body->GetA(); */ + ChVector xx = ChVector(x[0], x[1], x[2]); + ChVector local = ChTransform::TransformParentToLocal(xx, pos_last, rotq_last); + ChVector xNew = ChTransform::TransformLocalToParent(local, pos, rotq); + return xNew - xx; +} + +double cppRigidBody::hx(double* x, double t) +{ + /* rotm = body->GetA(); */ + ChVector local = ChTransform::TransformParentToLocal(ChVector(x[0],x[1],x[2]), pos_last, rotq_last); + ChVector xNew = ChTransform::TransformLocalToParent(local, pos, rotq); + return xNew.x() - x[0]; +} + +double cppRigidBody::hy(double* x, double t) +{ + /* rotm = body->GetA(); */ + ChVector local = ChTransform::TransformParentToLocal(ChVector(x[0],x[1],x[2]), pos_last, rotq_last); + ChVector xNew = ChTransform::TransformLocalToParent(local, pos, rotq); + return xNew.y() - x[1]; +} + +double cppRigidBody::hz(double* x, double t) +{ + /* rotm = body->GetA(); */ + ChVector local = ChTransform::TransformParentToLocal(ChVector(x[0],x[1],x[2]), pos_last, rotq_last); + ChVector xNew = ChTransform::TransformLocalToParent(local, pos, rotq); + return xNew.z() - x[2]; +} + +void cppRigidBody::prestep(double* force, double* torque) +{ + /* step to call before running chrono system step */ + pos_last = body->GetPos(); + vel_last = body->GetPos_dt(); + acc_last = body->GetPos_dtdt(); + rotm_last = body->GetA(); + rotq_last = body->GetRot(); + angacc_last = body->GetWacc_loc(); + angvel_last = body->GetWvel_loc(); + F_last = body->Get_Xforce(); + M_last = body->Get_Xtorque(); + // apply external forces + body->Empty_forces_accumulators(); + // calculate opposite force of gravity if free_x is 0 + double forceG[3]={0.,0.,0.}; + if (free_x.x() == 0) {forceG[0] = -system->system.Get_G_acc().x()*body->GetMass();} + if (free_x.y() == 0) {forceG[1] = -system->system.Get_G_acc().y()*body->GetMass();} + if (free_x.z() == 0) {forceG[2] = -system->system.Get_G_acc().z()*body->GetMass();} + body->Accumulate_force(ChVector(forceG[0]+force[0]*free_x.x(), + forceG[1]+force[1]*free_x.y(), + forceG[2]+force[2]*free_x.z()), + pos_last, + false); + body->Accumulate_torque(ChVector(torque[0]*free_r.x(), + torque[1]*free_r.y(), + torque[2]*free_r.z()), + true); + if (spring!=0) { + double spring_length = spring->Get_SpringLength(); + if (spring_length < mooring_restlength) { + spring->SetDisabled(true);//Set_SpringRestLength(spring_length); + } + else { + spring->SetDisabled(false);//Set_SpringRestLength(mooring_restlength); + } + } +} + + + +void cppRigidBody::poststep() +{ + pos = body->GetPos(); + vel = body->GetPos_dt(); + acc = body->GetPos_dtdt(); + rotm = body->GetA(); + rotq = body->GetRot(); + angacc = body->GetWacc_loc(); + angvel = body->GetWvel_loc(); + F = body->Get_Xforce(); + M = body->Get_Xtorque(); +} + +void cppRigidBody::setPrescribedMotionPoly(double coeff1) { + auto fixed_body = std::make_shared(); + fixed_body->SetPos(body->GetPos()); + fixed_body->SetBodyFixed(true); + system->system.Add(fixed_body); + auto lock = std::make_shared(); + lock->Initialize(body, fixed_body, fixed_body->GetCoord()); + system->system.Add(lock); + auto forced_motion = std::make_shared(); + forced_motion->Set_order(1); + forced_motion->Set_coeff(coeff1, 1); + std::shared_ptr forced_ptr = forced_motion; + lock->SetMotion_X(forced_ptr); +} + + +void cppRigidBody::setPrescribedMotionSine(double a, double f) { + auto fixed_body = std::make_shared(); + fixed_body->SetPos(body->GetPos()); + fixed_body->SetBodyFixed(true); + system->system.Add(fixed_body); + auto lock = std::make_shared(); + lock->Initialize(body, fixed_body, fixed_body->GetCoord()); + system->system.Add(lock); + auto forced_motion = std::make_shared(); + forced_motion->Set_amp(a); + forced_motion->Set_freq(f); + std::shared_ptr forced_ptr = forced_motion; + lock->SetMotion_X(forced_ptr); +} + +void cppRigidBody::setPosition(double* position){ + body->SetPos(ChVector<>(position[0], position[1], position[2])); +} + +void cppRigidBody::setRotation(double* quat) { + body->SetRot(ChQuaternion(quat[0], quat[1], quat[2], quat[3])); +} + +void cppRigidBody::setConstraints(double* free_x_in, double* free_r_in){ + free_x = ChVector<>(free_x_in[0], free_x_in[1], free_x_in[2]); + free_r = ChVector<>(free_r_in[0], free_r_in[1], free_r_in[2]); +} + +void cppRigidBody::setInertiaXX(double* inertia){ + body->SetInertiaXX(ChVector<>(inertia[0], inertia[1], inertia[2])); +} + + +void cppRigidBody::addSpring(double stiffness, + double damping, + double* fairlead, + double* anchor, + double rest_length) +{ + mooring_restlength = rest_length; + spring = std::make_shared(); + std::shared_ptr anchor_body = std::make_shared(); + anchor_body->SetPos(ChVector<>(anchor[0], anchor[1], anchor[2])); + anchor_body->SetBodyFixed(true); + system->system.AddBody(anchor_body); + spring->Initialize(body, + anchor_body, + true, // true for pos relative to bodies + ChVector<>(fairlead[0], fairlead[1], fairlead[2]), + ChVector<>(0.,0.,0.), + false, // true for auto rest length (distance between body1 and body2) + rest_length); + spring->Set_SpringK(stiffness); + spring->Set_SpringR(damping); + system->system.AddLink(spring); +} + +void cppRigidBody::addPrismaticLinksWithSpring(double* pris1, + double* pris2, + double stiffness, + double damping, + double rest_length) +{ + mooring_restlength = rest_length; + auto fairlead = std::make_shared(); + fairlead->SetName("PRIS3"); + fairlead->SetPos(body->GetPos()); + fairlead->SetMass(0.00001); + system->system.AddBody(fairlead); + auto mybod2 = std::make_shared(); + mybod2->SetName("PRIS1"); + mybod2->SetPos(ChVector<>(pris1[0], pris1[1], pris1[2])); + mybod2->SetMass(0.00001); + //mybod2->AddForce(-system->system.Get_G_acc()); + //mybod2->SetBodyFixed(true); + system->system.AddBody(mybod2); + auto mybod3 = std::make_shared(); + mybod3->SetName("PRIS2"); + mybod3->SetPos(ChVector<>(pris2[0], pris2[1], pris2[2])); + mybod3->SetBodyFixed(true); + system->system.AddBody(mybod3); + + auto mylink1 = std::make_shared(); + system->system.AddLink(mylink1); + auto mycoordsys1 = ChCoordsys<>(mybod2->GetPos(),Q_from_AngAxis(CH_C_PI/2., VECT_Y));//Q_from_AngAxis(CH_C_PI / 2, VECT_X)); + mylink1->Initialize(fairlead, mybod2, mycoordsys1); + + + + auto mylink2 = std::make_shared(); + system->system.AddLink(mylink2); + auto mycoordsys2 = ChCoordsys<>(mybod3->GetPos(),Q_from_AngAxis(CH_C_PI/2., VECT_X));//Q_from_AngAxis(CH_C_PI / 2, VECT_X)); + mylink2->Initialize(mybod2, mybod3,mycoordsys2); + + auto mylink3 = std::make_shared(); + //auto mylink3 = std::make_shared(); + //mylink3->SetMotion_axis(ChVector<>(0.,1.,0.)); + system->system.AddLink(mylink3); + mylink3->Initialize(fairlead, body, false, fairlead->GetCoord(), body->GetCoord()); + + + + spring = std::make_shared(); + spring->Initialize(fairlead, + mybod2, + true, // true for pos relative to bodies + ChVector<>(0.,0.,0.), + ChVector<>(0.,0.,0.), + false, + rest_length); // true for auto rest length (distance between body1 and body2)); + spring->Set_SpringK(stiffness); + spring->Set_SpringR(damping); + spring->SetName("SPRING1"); + system->system.AddLink(spring); +} + +void cppRigidBody::setName(std::string name) { + body->SetNameString(name); +} + + +cppSystem * newSystem(double* gravity) +{ + return new cppSystem(gravity); +} + + + +cppRigidBody * newRigidBody(cppSystem* system) +{ + return new cppRigidBody(system); +} + + diff --git a/proteus/mbd/ChRigidBody.pyx b/proteus/mbd/ChRigidBody.pyx new file mode 100644 index 0000000000..232f2efc6c --- /dev/null +++ b/proteus/mbd/ChRigidBody.pyx @@ -0,0 +1,2290 @@ +# distutils: language = c++ + + +# TO DO: +# get velocity gradients for added mass force (moorings) + +import os +import sys +import csv +cimport numpy as np +import numpy as np +from mpi4py import MPI +from scipy import spatial +from proteus import AuxiliaryVariables, Archiver, Comm, Profiling +from proteus import SpatialTools as st +from libcpp.string cimport string +from libcpp.vector cimport vector +from libcpp cimport bool +from libcpp.memory cimport (shared_ptr, + make_shared) +from collections import OrderedDict +from cython.operator cimport dereference as deref +# chrono C++ headers +cimport ChronoHeaders as ch +# chrono Python headers +from proteus.mbd cimport pyChronoCore as pych +from proteus.mprans import BodyDynamics as bd + + +cdef extern from "ChMoorings.h": + cdef cppclass cppMesh: + shared_ptr[ch.ChMesh] mesh + void SetAutomaticGravity(bool val) + cppMesh * newMesh(ch.ChSystemSMC&, shared_ptr[ch.ChMesh]) + cdef cppclass cppCable: + ch.ChSystemSMC& system + ch.ChMesh& mesh + vector[shared_ptr[ch.ChNodeFEAxyzDD]] nodes + vector[shared_ptr[ch.ChNodeFEAxyzrot]] nodesRot + vector[shared_ptr[ch.ChElementBeamANCF]] elems + vector[ch.ChVector] forces_drag + vector[ch.ChVector] forces_addedmass + double L0 + double length + int nb_elems + vector[ch.ChVector] mvecs + void buildNodes() + void buildMaterials() + void buildElements() + void buildMesh() + void updateDragForces() + void updateBuoyancyForces() + void setDragCoefficients(double Cd_axial, double Cd_normal) + void setAddedMassCoefficients(double Cd_axial, double Cd_normal) + void setIyy(double Iyy_in) + cdef cppclass cppMultiSegmentedCable: + ch.ChSystemSMC& system + ch.ChMesh& mesh + vector[shared_ptr[cppCable]] cables + vector[shared_ptr[ch.ChNodeFEAxyzDD]] nodes + vector[shared_ptr[ch.ChNodeFEAxyzrot]] nodesRot + vector[shared_ptr[ch.ChElementBeamANCF]] elems + shared_ptr[ch.ChLinkPointFrame] constraint_back + shared_ptr[ch.ChLinkPointFrame] constraint_front + vector[ch.ChVector] forces_drag + vector[ch.ChVector] forces_addedmass + void buildNodes() + void buildCable() + # void setVelocityAtNodes(double* fluid_velocity) + void attachFrontNodeToBody(shared_ptr[ch.ChBody]) + void attachBackNodeToBody(shared_ptr[ch.ChBody]) + void updateDragForces() + void updateAddedMassForces() + void applyForces() + void updateBuoyancyForces() + void setFluidVelocityAtNodes(vector[ch.ChVector] fluid_velocity) + void setFluidAccelerationAtNodes(vector[ch.ChVector] fluid_acceleration) + void setFluidDensityAtNodes(vector[double] dens) + void setContactMaterial(shared_ptr[ch.ChMaterialSurfaceSMC] material) + ch.ChVector getTensionElement(int i) + cppMultiSegmentedCable * newMoorings(ch.ChSystemSMC& system, + shared_ptr[ch.ChMesh] mesh, + vector[double] length, + vector[int] nb_elems, + vector[double] d, + vector[double] rho, + vector[double] E, + string beam_type + ) + cdef cppclass cppSurfaceBoxNodesCloud: + ch.ChSystemSMC& system + ch.ChVector position + ch.ChVector dimensions + shared_ptr[ch.ChBodyEasyBox] body; + void setNodesSize(double size) + cppSurfaceBoxNodesCloud * newSurfaceBoxNodesCloud(ch.ChSystemSMC& system, + shared_ptr[ch.ChMesh] mesh, + ch.ChVector position, + ch.ChVector dimensions) + + +cdef extern from "ChRigidBody.h": + cdef cppclass cppSystem: + ch.ChSystemSMC system + void DoStepDynamics(dt) + void step(double proteus_dt, int n_substeps) + void recordBodyList() + void setChTimeStep(double dt) + void setGravity(double* gravity) + void setDirectory(string directory) + void setTimestepperType(string tstype, bool verbose) + cppSystem * newSystem(double* gravity) + cdef cppclass cppRigidBody: + shared_ptr[ch.ChBody] body + double mass + ch.ChVector pos + ch.ChVector pos_last + ch.ChVector vel + ch.ChVector vel_last + ch.ChVector acc + ch.ChVector acc_last + ch.ChVector angvel + ch.ChVector angvel_last + ch.ChVector angacc + ch.ChVector angacc_last + # ChVector inertia + ch.ChMatrix33 rotm + ch.ChMatrix33 rotm_last + ch.ChQuaternion rotq + ch.ChQuaternion rotq_last + # double* free_x + # double* free_r + ch.ChVector F + ch.ChVector F_last + ch.ChVector M + ch.ChVector M_last + cppRigidBody(cppSystem* system) + void prestep(double* force, double* torque) + void poststep() + ch.ChVector hxyz(double* x, double dt) + double hx(double* x, double dt) + double hy(double* x, double dt) + double hz(double* x, double dt) + void addSpring(double stiffness, + double damping, + double* fairlead, + double* anchor, + double rest_length) + void addPrismaticLinksWithSpring(double* pris1, + double* pris2, + double stiffness, + double damping, + double rest_length); + void setRotation(double* quat) + void setPosition(double* pos) + void setConstraints(double* free_x, double* free_r) + void setInertiaXX(double* inertia) + void setName(string name) + void setPrescribedMotionPoly(double coeff1) + void setPrescribedMotionSine(double a, double f) + + + cppRigidBody * newRigidBody(cppSystem* system) + +cdef class ProtChBody: + cdef cppRigidBody * thisptr + cdef ch.ChQuaternion rotation + cdef ch.ChQuaternion rotation_last + cdef public: + str record_file + object model + ProtChSystem ProtChSystem + object Shape + int nd, i_start, i_end + double dt + double width_2D + object record_dict + object prescribed_motion_function + pych.ChBody ChBody + np.ndarray position + np.ndarray position_last + np.ndarray F + np.ndarray M + np.ndarray F_last + np.ndarray M_last + np.ndarray F_prot + np.ndarray M_prot + np.ndarray F_prot_last + np.ndarray M_prot_last + np.ndarray F_applied + np.ndarray M_applied + np.ndarray F_applied_last + np.ndarray M_applied_last + np.ndarray acceleration + np.ndarray acceleration_last + np.ndarray velocity + np.ndarray velocity_last + np.ndarray ang_acceleration_last + np.ndarray ang_acceleration + np.ndarray ang_velocity_last + np.ndarray ang_velocity + double ang_vel_norm + double ang_vel_norm_last + np.ndarray barycenter0 + np.ndarray rotation_init + np.ndarray rotm + np.ndarray rotm_last + np.ndarray rotq + np.ndarray rotq_last + np.ndarray adams_vel + string name + bool predicted + double dt_predict + np.ndarray h_predict # predicted displacement + double h_ang_predict # predicted angular displacement (angle) + np.ndarray h_ang_vel_predict # predicted angular velocity + np.ndarray h_predict_last # predicted displacement + double h_ang_predict_last # predicted angular displacement (angle) + np.ndarray h_ang_vel_predict_last # predicted angular velocity + # np.ndarray free_r + # np.ndarray free_x + def __cinit__(self, + ProtChSystem system, + shape=None, + nd=None, sampleRate=0): + self.ProtChSystem = system + self.thisptr = newRigidBody(system.thisptr) + self.ChBody = pych.ChBody() + self.thisptr.body = self.ChBody.sharedptr_chbody # give pointer to cpp class + self.ProtChSystem.thisptr.system.AddBody(self.thisptr.body) + self.Shape = None + self.setRotation(np.array([1.,0.,0.,0.])) # initialise rotation (nan otherwise) + self.attachShape(shape) # attach shape (if any) + self.ProtChSystem.addSubcomponent(self) # add body to system (for pre and post steps) + self.record_dict = OrderedDict() + self.F_prot = np.zeros(3) # initialise empty Proteus force + self.M_prot = np.zeros(3) # initialise empty Proteus moment + self.F_applied = np.zeros(3) # initialise empty Applied force + self.M_applied = np.zeros(3) # initialise empty Applied moment + self.prescribed_motion_function = None + + self.velocity = np.zeros(3) + self.velocity_last = np.zeros(3) + self.ang_velocity = np.zeros(3) + self.position_last = self.position + self.ang_vel_norm = 0. # used for mesh disp prediction + self.ang_vel_norm_last = 0. # used for mesh disp prediction + self.h_predict = np.zeros(3) + self.h_ang_predict = 0. + self.h_ang_vel_predict = np.zeros(3) + self.h_predict_last = np.zeros(3) + self.h_ang_predict_last = 0. + self.h_ang_vel_predict_last = np.zeros(3) + self.predicted = False + self.adams_vel = np.zeros((5, 3)) + self.ChBody.SetBodyFixed(False) + # if self.nd is None: + # assert nd is not None, "must set nd if SpatialTools.Shape is not passed" + # self.nd = nd + + def attachShape(self, shape): + """Attach proteus.SpatialTools shape to body. + Used for automatic calculation of external forces from Proteus. + Called automatically when creating a body and passing a shape + instance. + + Parameters + ---------- + shape: SpatialTools.Shape + instance of shape from proteus.SpatialTools or + proteus.mprans.SpatialTools + """ + if shape is not None: + assert self.Shape is None, 'Shape '+self.Shape.name+' was already attached' + self.Shape = shape + if 'ChRigidBody' not in shape.auxiliaryVariables: + shape.auxiliaryVariables['ChRigidBody'] = self + self.setName(shape.name) + self.nd = shape.Domain.nd + self.SetPosition(shape.barycenter) + + def SetBodyFixed(self, bool state): + """Fix body in space + + Parameters + ---------- + state: bool + body fixed if True, body free if False + """ + deref(self.thisptr.body).SetBodyFixed(state) + + def setWidth2D(self, width): + """Sets width of 2D body (for forces and moments calculation) + + Parameters + ---------- + width: float + width of the body + """ + self.width_2D = width + + def set_indices(self, i_start, i_end): + """Sets the flags of the boundaries of the body + numbers must be gloabal (from domain.segmentFlags or + domain.facetFlags) and a range from i_start to i_end. + + Parameters + ---------- + i_start: int + first global flag of body boundaries + i_end: int + last global flag (+1) of body boundaries + """ + self.i_start = i_start + self.i_end = i_end + + def attachAuxiliaryVariables(self,avDict): + pass + + def setInertiaXX(self, np.ndarray inertia): + self.thisptr.setInertiaXX( inertia.data) + + def setInitialRot(self, rot): + cdef np.ndarray zeros = np.zeros(3) + self.rotation_init = rot + self.thisptr.prestep( zeros.data, + zeros.data) + if self.rotation_init is not None: + Profiling.logEvent("$$$$$ SETTING ROT") + self.thisptr.setRotation( self.rotation_init.data) + self.thisptr.poststep() + + def hxyz(self, np.ndarray x, double t, debug=False): + cdef np.ndarray h + cdef np.ndarray xx + cdef double ang, and_last + cdef np.ndarray d_tra, d_tra_last # translational displacements + cdef np.ndarray d_rot, d_rot_last # rotational displacements + cdef np.ndarray h_body # displacement from body + cdef ch.ChVector h_body_vec + h = np.zeros(3) + if self.predicted is False: + self.prediction() + # if self.ProtChSystem.thisptr.system.GetChTime() > 0.0003: + # if self.ProtChSystem.step_nb > self.ProtChSystem.step_start: + # self.ChBody.SetBodyFixed(False) + if self.ProtChSystem.scheme == "CSS": + h_body_vec = self.thisptr.hxyz( x.data, t) + h_body = pych.ChVector_to_npArray(h_body_vec) + h += h_body + elif self.ProtChSystem.scheme == "ISS": + # remove previous prediction + # translate back first + if debug is True: + print("$$$$$$$$$$$$$$$$$$") + print("x: ", x) + # d_tra_last = -self.velocity_last*dt_half_last + # h += d_tra_last + h += -self.h_predict_last + if debug is True: + print("h_predict_last: ", self.h_predict_last) + print("h_predict: ", self.h_predict) + print("h_ang_predict_last: ", self.h_ang_predict_last) + print("h_ang_predict: ", self.h_ang_predict) + print("h_ang_vel_predict_last: ", self.h_ang_vel_predict_last) + print("h_ang_vel_predict: ", self.h_ang_vel_predict) + # rotate back + # ang_last = -self.ang_vel_norm_last*dt_half_last + ang_last = -self.h_ang_predict_last + if ang > 0: + d_rot_last = (st.rotation3D(points=x+h, # (translated back) + rot=ang_last, + axis=self.h_ang_vel_predict_last, + pivot=self.position_last) + -x+h) + h += d_rot_last + # add rigid body displacement + xx = x+h # previous position of body + if debug is True: + print("x_old: ", x+h) + print("F_applied: ", self.F_applied) + h_body_vec = self.thisptr.hxyz( xx.data, t) + h_body = pych.ChVector_to_npArray(h_body_vec) + h += h_body + # add current prediction + # rotate first + # ang = self.ang_vel_norm*dt_half + ang = self.h_ang_predict + if debug is True: + print("x_body: ", x+h) + print("pos_body: ", self.ChBody.GetPos()) + if ang > 0: + d_rot = (st.rotation3D(points=x+h, + rot=ang, + axis=self.h_ang_vel_predict, + pivot=self.position) + -x+h) + h += d_rot + if debug is True: + print("x_new_rot: ", x+h) + # translate + # d_tra = self.velocity*dt_half + # h += d_tra + h += self.h_predict + if debug is True: + print("x_new_trarot: ", x+h) + comm = Comm.get().comm.tompi4py() + # print(comm.rank, h, x, t) + return h + + def hx(self, np.ndarray x, double t): + """BC function for mesh nodes displacement (x component) + + Parameters + ---------- + x: array_like + coordinates of the node before displacement + t: double + simulation time + """ + return self.hxyz(x, t)[0] + + def hy(self, np.ndarray x, double t): + """BC function for mesh nodes displacement (y component) + + Parameters + ---------- + x: array_like + coordinates of the node before displacement + t: double + simulation time + """ + return self.hxyz(x, t)[1] + + def hz(self, np.ndarray x, double t): + """BC function for mesh nodes displacement (z component) + + Parameters + ---------- + x: array_like + coordinates of the node before displacement + t: double + simulation time + """ + return self.hxyz(x, t)[2] + + def hx_translation(self, np.ndarray x, double t): + """BC function for mesh nodes displacement (x component) + + Parameters + ---------- + x: array_like + coordinates of the node before displacement + t: double + simulation time + """ + return (self.position-self.position_last)[0] + + def hy_translation(self, np.ndarray x, double t): + """BC function for mesh nodes displacement (y component) + + Parameters + ---------- + x: array_like + coordinates of the node before displacement + t: double + simulation time + """ + return (self.position-self.position_last)[1] + + def hz_translation(self, np.ndarray x, double t): + """BC function for mesh nodes displacement (z component) + + Parameters + ---------- + x: array_like + coordinates of the node before displacement + t: double + simulation time + """ + return (self.position-self.position_last)[2] + + def hx_rotation(self, np.ndarray x, double t): + """BC function for mesh nodes displacement (x component) + + Parameters + ---------- + x: array_like + coordinates of the node before displacement + t: double + simulation time + """ + return self.hxyz(x, t)[0]-(self.position-self.position_last)[0] + + def hy_rotation(self, np.ndarray x, double t): + """BC function for mesh nodes displacement (y component) + + Parameters + ---------- + x: array_like + coordinates of the node before displacement + t: double + simulation time + """ + return self.hxyz(x, t)[1]-(self.position-self.position_last)[1] + + def hz_rotation(self, np.ndarray x, double t): + """BC function for mesh nodes displacement (z component) + + Parameters + ---------- + x: array_like + coordinates of the node before displacement + t: double + simulation time + """ + return self.hxyz(x, t)[2]-(self.position-self.position_last)[2] + + def addSpring(self, double stiffness, double damping, np.ndarray fairlead, + np.ndarray anchor, double rest_length): + self.thisptr.addSpring(stiffness, damping, fairlead.data, + anchor.data, rest_length) + + def SetPosition(self, np.ndarray position): + """Sets position of body manually + + Parameters + ---------- + position: array_like + new position of body (must be array of length 3) + """ + self.position = position + self.thisptr.setPosition( position.data) + + def SetPosition(self, np.ndarray position): + """Sets position of body manually + + Parameters + ---------- + position: array_like + new position of body (must be array of length 3) + """ + self.thisptr.setPosition( position.data) + + def setRotation(self, np.ndarray quaternion): + """Sets rotation of body manually + + Parameters + ---------- + rotation: array_like + new rotation of body (quaternion: must be array of length 4) + """ + self.thisptr.setRotation( quaternion.data) + + def setConstraints(self, np.ndarray free_x, np.ndarray free_r): + """Sets constraints on the body + (!) Only acts on Proteus and gravity forces + + Parameters + ---------- + free_x: array_like + Translational constraints. + free_r: array_like + Rotational constraints. + """ + self.thisptr.setConstraints( free_x.data, free_r.data) + + def SetMass(self, double mass): + """Sets mass of body. + + Parameters + ---------- + mass: double + mass of the body + """ + deref(self.thisptr.body).SetMass(mass) + + def getPressureForces(self): + """Gives pressure forces from fluid (Proteus) acting on body. + (!) Only works during proteus simulation + + Returns + ------- + F_p: array_like + pressure forces (x, y, z) as provided by Proteus + """ + i0, i1 = self.i_start, self.i_end + F_p = self.ProtChSystem.model.levelModelList[-1].coefficients.netForces_p[i0:i1, :] + F_t = np.sum(F_p, axis=0) + return F_t + + def getShearForces(self): + """Gives shear forces from fluid (Proteus) acting on body + (!) Only works during proteus simulation + + Returns + ------- + F_v: array_like + shear forces (x, y, z) as provided by Proteus + """ + i0, i1 = self.i_start, self.i_end + F_v = self.ProtChSystem.model.levelModelList[-1].coefficients.netForces_v[i0:i1, :] + F_t = np.sum(F_v, axis=0) + return F_t + + def getMoments(self): + """Gives moments from fluid (Proteus) acting on body + (!) Only works during proteus simulation + + Returns + ------- + M: array_like + moments (x, y, z) as provided by Proteus + """ + i0, i1 = self.i_start, self.i_end + M = self.ProtChSystem.model.levelModelList[-1].coefficients.netMoments[i0:i1, :] + M_t = np.sum(M, axis=0) + # !!!!!!!!!!!! UPDATE BARYCENTER !!!!!!!!!!!! + Fx, Fy, Fz = self.F_prot + rx, ry, rz = self.barycenter0-self.ChBody.GetPos() + Mp = np.array([ry*Fz-rz*Fy, -(rx*Fz-rz*Fx), (rx*Fy-ry*Fx)]) + M_t += Mp + return M_t + + def SetPos(self, double[:] pos): + """Sets current position of body + + Parameters + ---------- + pos: array_like + position of body (array of length 3) + """ + assert len(pos) == 3, 'Position aray must be of length 3' + cdef ch.ChVector pos_vec + pos_vec = ch.ChVector[double](pos[0], pos[1], pos[2]) + deref(self.thisptr.body).SetPos(pos_vec) + + def SetRot(self, double[:] rot): + """Sets current rotation (quaternion) of body + + Parameters + ---------- + rot: array_like + rotation of body (array of length 4) + """ + assert len(rot) == 4, 'Position aray must be of length 4' + cdef ch.ChQuaternion rot_vec + rot_vec = ch.ChQuaternion[double](rot[0], rot[1], rot[2], rot[3]) + deref(self.thisptr.body).SetRot(rot_vec) + + def getRotationMatrix(self): + """Gives current rotation (matrix) of body + + Returns + ------- + rot: array_like + current rotation (matrix) of body + """ + x0 = self.thisptr.rotm.Get_A_Xaxis().x() + x1 = self.thisptr.rotm.Get_A_Xaxis().y() + x2 = self.thisptr.rotm.Get_A_Xaxis().z() + y0 = self.thisptr.rotm.Get_A_Yaxis().x() + y1 = self.thisptr.rotm.Get_A_Yaxis().y() + y2 = self.thisptr.rotm.Get_A_Yaxis().z() + z0 = self.thisptr.rotm.Get_A_Zaxis().x() + z1 = self.thisptr.rotm.Get_A_Zaxis().y() + z2 = self.thisptr.rotm.Get_A_Zaxis().z() + rot = np.array([x0, x1, x2], + [y0, y1, y2], + [z0, z1, z2]) + return rot + + def prestep(self): + """Called before Chrono system step. + Sets external forces automatically from Proteus solution. + """ + self.storeValues() + # if self.ProtChSystem.thisptr.system.GetChTime() > 0.0003 or self.ProtChSystem.model is None: # CHANGE + # # if self.ProtChSystem.step_nb > self.ProtChSystem.step_start: + # self.ChBody.SetBodyFixed(False) + if self.ProtChSystem.model is not None: + self.F_prot = self.getPressureForces()+self.getShearForces() + self.M_prot = self.getMoments() + if self.ProtChSystem.first_step is True: + # just apply initial conditions for 1st time step + F_bar = self.F_prot + M_bar = self.M_prot + else: + # actual force applied to body + if self.ProtChSystem.prediction == "backwardEuler": + F_bar = self.F_prot + M_bar = self.M_prot + if self.ProtChSystem.prediction == "forwardEuler": + F_bar = self.F_prot_last + M_bar = self.M_prot_last + if self.ProtChSystem.prediction == "implicitOrder2": + F_bar = (self.F_prot+self.F_prot_last)/2. + M_bar = (self.M_prot+self.M_prot_last)/2. + # self.F_applied = self.F_prot + # self.M_applied = self.M_prot + # self.F_applied = 2*F_bar - self.F_applied_last + # self.M_applied = 2*M_bar - self.M_applied_last + F_solid_type = 1 + if F_solid_type == 1: + F_body = F_bar + M_body = M_bar + elif F_solid_type == 2: + if np.linalg.norm(self.F_prot_last) == 0: # first time step + F_body = F_bar + M_body = M_bar + else: + F_body = 2*F_bar-self.F_applied_last + M_body = 2*M_bar-self.M_applied_last + self.setExternalForces(F_body, M_body) + self.predicted = False + + def setExternalForces(self, np.ndarray forces, np.ndarray moments): + """Sets external forces to body. + Called during prestep or can be called manually. If called manually, + must be a Chrono only simulation. + + Parameters + ---------- + forces: array_like + forces array (length 3) + moments: array_like + moments array (length 3) + """ + self.F_applied = forces + self.M_applied = moments + self.thisptr.prestep( forces.data, + moments.data) + + def poststep(self): + """Called after Chrono system step. + Records values to csv, broadcast new position and rotation from + calculating processor to all processors for moving mesh BC. + """ + if self.prescribed_motion_function is not None: + new_x = self.callPrescribedMotion(self.ProtChSystem.model.stepController.t_model_last) + self.thisptr.setPosition( new_x.data) + self.thisptr.poststep() + self.getValues() + comm = Comm.get().comm.tompi4py() + cdef ch.ChQuaternion rotq + cdef ch.ChQuaternion rotq_last + cdef ch.ChVector pos + cdef ch.ChVector pos_last + cdef double e0, e1, e2, e3, e0_last, e1_last, e2_last, e3_last + cdef double posx, posy, posz, posx_last, posy_last, posz_last + if self.ProtChSystem.parallel_mode is True: + # need to broadcast values to all processors on the C++ side + # comm.Barrier() + self.rotq = comm.bcast(self.rotq, + self.ProtChSystem.chrono_processor) + self.rotq_last = comm.bcast(self.rotq_last, + self.ProtChSystem.chrono_processor) + self.position = comm.bcast(self.position, + self.ProtChSystem.chrono_processor) + self.position_last = comm.bcast(self.position_last, + self.ProtChSystem.chrono_processor) + if comm.rank == self.ProtChSystem.chrono_processor and self.ProtChSystem.record_values is True: + self._recordValues() + # need to pass position and rotation values to C++ side + # needed for transformations when calling hx, hy, hz, hxyz + e0, e1, e2, e3 = self.rotq + e0_last, e1_last, e2_last, e3_last = self.rotq_last + posx, posy, posz = self.position + posx_last, posy_last, posz_last = self.position_last + pos = ch.ChVector[double](posx, posy, posz) + pos_last = ch.ChVector[double](posx_last, posy_last, posz_last) + rotq = ch.ChQuaternion[double](e0, e1, e2, e3) + rotq_last = ch.ChQuaternion[double](e0_last, e1_last, e2_last, e3_last) + self.thisptr.rotq = rotq + self.thisptr.rotq_last = rotq_last + self.thisptr.pos = pos + self.thisptr.pos_last = pos_last + + def prediction(self): + comm = Comm.get().comm.tompi4py() + + + cdef ch.ChVector h_body_vec + h_body_vec = self.thisptr.hxyz( self.position_last.data, 0.) + #print("MY BODY DISP: ", h_body_vec.x(), h_body_vec.y(), h_body_vec.z()) + # if self.ProtChSystem.model is not None: + # try: + # dt = self.ProtChSystem.model.levelModelList[-1].dt_last + # except: + # dt = 0. + # print("$$$$$$$$$$$$$ ERROR") + # else: + # dt = self.ProtChSystem.dt_fluid + # dt_half = dt/2. + # # if self.ProtChSystem.scheme == "ISS": + # self.h_predict_last = self.h_predict + # self.h_ang_predict_last = self.h_ang_predict + # self.h_ang_vel_predict_last = self.h_ang_vel_predict + # # dt_half = self.ProtChSystem.dt_fluid/2. + # # dt_half = self.ProtChSystem.dt_fluid_next/2. + # # if substeps is False: + # self.h_predict = -self.velocity*dt_half + # print("$$$$$$$$$$$$$$$", comm.rank, self.h_predict, dt, self.velocity[1]) + # self.h_ang_predict = np.sqrt(self.ang_velocity[0]**2 + # +self.ang_velocity[1]**2 + # +self.ang_velocity[2]**2)*dt_half + # self.h_ang_vel_predict = self.ang_velocity + # self.adams_vel[1:] = self.adams_vel[:-1] + # self.adams_vel[0] = self.velocity + # av = self.adams_vel + # adams_bashforth = False + # if adams_bashforth == True: + # if np.linalg.norm(av[4]) != 0: + # Profiling.logEvent("$$$$$$$$$$$$$$$$$$$$$$$ ADAMS") + # h = 0+dt_half*(1901./720.*av[0] - 1387./360.*av[1] + 109./20.*av[2] - 637./360.*av[3] + 251./720.*av[4]) + # elif np.linalg.norm(av[3]) != 0: + # h = 0+dt_half*(55./24.*av[0] - 59./24.*av[1] + 37./24.*av[2] - 3./8.*av[3]) + # elif np.linalg.norm(av[2]) != 0: + # h = 0+dt_half*(23./12.*av[0] - 4./3.*av[1] + 5./12.*av[2]) + # elif np.linalg.norm(av[1]) != 0: + # h = 0+dt_half*(3./2.*av[0] - 1./2.*av[1]) + # else: + # h = 0+dt_half*av[0] + # # else: + # # nsteps = max(int(dt_half/self.ProtChSystem.chrono_dt), 1) + # # if nsteps < self.ProtChSystem.min_nb_steps: + # # nsteps = self.ProtChSystem.min_nb_steps + # # h = np.zeros(3) + # # h_ang = np.zeros(3) + # # v = self.velocity.copy() + # # v_ang = self.ang_velocity.copy() + # # for i in range(nsteps): + # # h, v = bd.forward_euler(h, v, self.acceleration, dt_half/nsteps) + # # h_ang, v_ang = bd.forward_euler(h_ang, v_ang, self.ang_acceleration, dt_half/nsteps) + # # self.h_predict = h + # # self.h_ang_predict = np.sqrt(h_ang[0]**2+h_ang[1]**2+h_ang[2]**2) + # # self.h_ang_vel_predict = v_ang + # # h_body_vec = self.thisptr.hxyz( self.position_last.data, 0) + # # h_body = pych.ChVector_to_npArray(h_body_vec) + # # if self.ProtChSystem.dt != 0: + # # vel_predict = h_body/(self.ProtChSystem.dt) + # # self.h_predict = vel_predict*dt_half + # # else: + # # self.h_predict = np.zeros(3) + # # print("ADDED: ", self.h_predict, dt_half) + # # print("BODY H:", h_body, self.velocity, self.ChBody.GetPos_dt()) + # self.h_predict = h + self.predicted = True + + def calculate_init(self): + """Called from self.ProtChSystem.calculate_init() + before simulation starts + """ + # barycenter0 used for moment calculations + if self.Shape is not None: + self.barycenter0 = self.Shape.barycenter.copy() + # get the initial values for F and M + cdef np.ndarray zeros = np.zeros(3) + self.setExternalForces(zeros, zeros) + self.thisptr.poststep() + # get first, store then on initial time step + self.getValues() + self.storeValues() + # self.thisptr.setRotation( self.rotation_init.data) + # + + def calculate(self): + pass + + def setPrescribedMotionSine(self, double a, double f): + """Sets sinusoidal prescribed motion for body + + Parameters + ---------- + a: double + amplitude of sinusoid + f: double + frequency of sinusoid + """ + self.thisptr.setPrescribedMotionSine(a, f) + + def setPrescribedMotionPoly(self, double coeff1): + """Sets polynomial prescribed motion for body + + Parameters + ---------- + coeff1: double + coeff1 of polynomial + """ + self.thisptr.setPrescribedMotionPoly(coeff1) + + def setPrescribedMotion(self, function): + """Sets custom prescribed motion function + (!) should be preferably set only if body is free and not + linked to other bodies or other elements (such as moorings) + as this is used only for setting moving mesh BC by enforcing + position of the body at each time step. + Use setPrescribedMotionPoly or setPrescribedMotionSine for + functions that are safe to use with a body linked with other + elements. + + Parameters + ---------- + function: + must be a function of time returning an array of the + absolute position of the body (numpy array of length 3: + x, y, z) + """ + self.prescribed_motion_function = function + + cdef np.ndarray callPrescribedMotion(self, double t): + return self.prescribed_motion_function(t) + + def storeValues(self): + self.velocity_last = self.velocity + self.position_last = self.position + self.acceleration_last = self.acceleration + self.rotq_last = self.rotq + self.rotm_last = self.rotm + self.ang_acceleration_last = self.ang_acceleration + self.ang_velocity_last = self.ang_velocity + self.F_last = self.F + self.M_last = self.M + self.ang_vel_norm_last = self.ang_vel_norm + # force from fluid at current time step + self.F_prot_last = np.array(self.F_prot) + self.M_prot_last = np.array(self.M_prot) + self.F_applied_last = np.array(self.F_applied) + self.M_applied_last = np.array(self.M_applied) + if self.ProtChSystem.parallel_mode is True: + comm = Comm.get().comm.tompi4py() + self.position_last = comm.bcast(self.position_last, + self.ProtChSystem.chrono_processor) + self.velocity_last = comm.bcast(self.velocity_last, + self.ProtChSystem.chrono_processor) + self.acceleration_last = comm.bcast(self.acceleration_last, + self.ProtChSystem.chrono_processor) + self.rotq_last = comm.bcast(self.rotq_last, + self.ProtChSystem.chrono_processor) + self.rotm_last = comm.bcast(self.rotm_last, + self.ProtChSystem.chrono_processor) + self.ang_velocity_last = comm.bcast(self.ang_velocity_last, + self.ProtChSystem.chrono_processor) + self.ang_acceleration_last = comm.bcast(self.ang_acceleration_last, + self.ProtChSystem.chrono_processor) + self.ang_vel_norm_last = comm.bcast(self.ang_vel_norm_last, + self.ProtChSystem.chrono_processor) + self.F_prot_last = comm.bcast(self.F_prot_last, + self.ProtChSystem.chrono_processor) + self.M_prot_last = comm.bcast(self.M_prot_last, + self.ProtChSystem.chrono_processor) + self.F_applied_last = comm.bcast(self.F_applied_last, + self.ProtChSystem.chrono_processor) + self.M_applied_last = comm.bcast(self.M_applied_last, + self.ProtChSystem.chrono_processor) + + def getValues(self): + """Get values (pos, vel, acc, etc.) from C++ to python + """ + # position + self.position = self.ChBody.GetPos() + # rotation + self.rotq = self.ChBody.GetRot() + self.rotm = self.ChBody.GetA() + # acceleration + self.acceleration = self.ChBody.GetPos_dtdt() + #velocity + self.velocity = self.ChBody.GetPos_dt() + # angular acceleration + self.ang_acceleration = self.ChBody.GetWacc_loc() + # angular velocity + self.ang_velocity = self.ChBody.GetWvel_loc() + # norm of angular velocity + self.ang_vel_norm = np.sqrt(self.ang_velocity[0]**2 + +self.ang_velocity[1]**2 + +self.ang_velocity[2]**2) + # force + self.F = pych.ChVector_to_npArray(self.thisptr.F) + # moment + self.M = pych.ChVector_to_npArray(self.thisptr.M) + if self.ProtChSystem.parallel_mode is True: + comm = Comm.get().comm.tompi4py() + self.position = comm.bcast(self.position, self.ProtChSystem.chrono_processor) + self.velocity = comm.bcast(self.velocity, self.ProtChSystem.chrono_processor) + self.acceleration = comm.bcast(self.acceleration, self.ProtChSystem.chrono_processor) + self.rotq = comm.bcast(self.rotq, self.ProtChSystem.chrono_processor) + self.rotm = comm.bcast(self.rotm, self.ProtChSystem.chrono_processor) + self.ang_velocity = comm.bcast(self.ang_velocity, self.ProtChSystem.chrono_processor) + self.ang_acceleration = comm.bcast(self.ang_acceleration, self.ProtChSystem.chrono_processor) + self.ang_vel_norm = comm.bcast(self.ang_vel_norm, self.ProtChSystem.chrono_processor) + + + def setRecordValues(self, filename=None, all_values=False, pos=False, + rot=False, ang_disp=False, F=False, M=False, + inertia=False, vel=False, acc=False, ang_vel=False, + ang_acc=False, h_predict=False): + """ + Sets the body attributes that are to be recorded in a csv file + during the simulation. + + Parameters + ---------- + filename: Optional[string] + Name of file, if not set, the file will be named as follows: + 'record_[shape.name].csv' + all_values: bool + Set to True to record all values listed below. + time: bool + Time of recorded row (default: True). + pos: bool + Position of body (default: False. Set to True to record). + rot: bool + Rotation of body (default: False. Set to True to record). + ang_disp: array + Angular displecement calculated during rigid body calculation step. + Applied on the body in order to make it rotating. + F: bool + Forces applied on body (default: False. Set to True to record). + M: bool + Moments applied on body (default: False. Set to True to record). + inertia: bool + Inertia of body (default: False. Set to True to record). + vel: bool + Velocity of body (default: False. Set to True to record). + acc: bool + Acceleration of body (default: False. Set to True to record). + ang_vel: array + Angular velocity of body (default: False. Set to True to record). + ang_acc: bool + Angular acceleration of body (default: False. Set to True to record). + Notes + ----- + To add another value manually, add to dictionary self.record_dict: + key: header of the column in .csv + value: list of length 2: [variable name, index within variable] + (if no index, use None) + e.g. self.record_dict['m']['mass', None] + """ + if all_values is True: + pos = rot = F = M = acc = vel = ang_acc = ang_vel = h_predict = True + if pos is True: + self.record_dict['x'] = ['position', 0] + self.record_dict['y'] = ['position', 1] + self.record_dict['z'] = ['position', 2] + if rot is True: + self.record_dict['rotq_e0'] = ['rotq', 0] + self.record_dict['rotq_e1'] = ['rotq', 1] + self.record_dict['rotq_e2'] = ['rotq', 2] + self.record_dict['rotq_e3'] = ['rotq', 3] + if F is True: + self.record_dict['Fx'] = ['F', 0] + self.record_dict['Fy'] = ['F', 1] + self.record_dict['Fz'] = ['F', 2] + self.record_dict['Fx_prot'] = ['F_prot', 0] + self.record_dict['Fy_prot'] = ['F_prot', 1] + self.record_dict['Fz_prot'] = ['F_prot', 2] + self.record_dict['Fx_applied'] = ['F_applied', 0] + self.record_dict['Fy_applied'] = ['F_applied', 1] + self.record_dict['Fz_applied'] = ['F_applied', 2] + Fx = Fy = Fz = True + if M is True: + self.record_dict['Mx'] = ['M', 0] + self.record_dict['My'] = ['M', 1] + self.record_dict['Mz'] = ['M', 2] + self.record_dict['Mx_prot'] = ['M_prot', 0] + self.record_dict['My_prot'] = ['M_prot', 1] + self.record_dict['Mz_prot'] = ['M_prot', 2] + self.record_dict['Mx_applied'] = ['M_applied', 0] + self.record_dict['My_applied'] = ['M_applied', 1] + self.record_dict['Mz_applied'] = ['M_applied', 2] + if acc is True: + self.record_dict['ax'] = ['acceleration', 0] + self.record_dict['ay'] = ['acceleration', 1] + self.record_dict['az'] = ['acceleration', 2] + if vel is True: + self.record_dict['ux'] = ['velocity', 0] + self.record_dict['uy'] = ['velocity', 1] + self.record_dict['uz'] = ['velocity', 2] + if ang_acc is True: + self.record_dict['ang_ax'] = ['ang_acceleration', 0] + self.record_dict['ang_ay'] = ['ang_acceleration', 1] + self.record_dict['ang_az'] = ['ang_acceleration', 2] + if ang_vel is True: + self.record_dict['ang_ux'] = ['ang_velocity', 0] + self.record_dict['ang_uy'] = ['ang_velocity', 1] + self.record_dict['ang_uz'] = ['ang_velocity', 2] + if inertia is True: + self.record_dict['intertia'] = ['inertia', None] + if h_predict is True: + self.record_dict['hx'] = ['h_predict', 0] + self.record_dict['hy'] = ['h_predict', 1] + self.record_dict['hz'] = ['h_predict', 2] + + def _recordValues(self): + """Records values of body attributes in a csv file. + """ + if self.Shape is not None: + self.record_file = os.path.join(Profiling.logDir, 'record_' + self.Shape.name + '.csv') + else: + self.record_file = os.path.join(Profiling.logDir, 'record_' + 'body' + '.csv') + t_chrono = self.ProtChSystem.thisptr.system.GetChTime() + if self.ProtChSystem.model is not None: + t_last = self.ProtChSystem.model.stepController.t_model_last + try: + dt_last = self.ProtChSystem.model.levelModelList[-1].dt_last + except: + dt_last = 0 + t = t_last + else: + t = t_chrono + t_sim = Profiling.time()-Profiling.startTime + values_towrite = [t, t_chrono, t_sim] + if t == 0: + headers = ['t', 't_ch', 't_sim'] + for key in self.record_dict: + headers += [key] + with open(self.record_file, 'w') as csvfile: + writer = csv.writer(csvfile, delimiter=',') + writer.writerow(headers) + for key, val in self.record_dict.iteritems(): + if val[1] is not None: + values_towrite += [getattr(self, val[0])[val[1]]] + else: + values_towrite += [getattr(self, val[0])] + with open(self.record_file, 'a') as csvfile: + writer = csv.writer(csvfile, delimiter=',') + writer.writerow(values_towrite) + + def addPrismaticLinksWithSpring(self, np.ndarray pris1, + np.ndarray pris2, double stiffness, double damping, + double rest_length): + """ + fairlead: barycenter coords + pris: absolute coords + pris1-------fairlead(barycenter) + | + | + | + | + pris2 + """ + self.thisptr.addPrismaticLinksWithSpring( pris1.data, + pris2.data, + stiffness, + damping, + rest_length) + + def setName(self, string name): + """Sets name of body (used for csv file) + + Parameters + ---------- + name: str + name of the body + """ + self.name = name + self.thisptr.setName(name) + + +cdef class ProtChSystem: + cdef cppSystem * thisptr + cdef public object model + cdef object subcomponents + cdef public double dt_init + cdef double proteus_dt + cdef double proteus_dt_last + cdef double proteus_dt_next + cdef double chrono_dt + cdef string directory + cdef object u + cdef int nd + cdef object femSpace_velocity + cdef object femSpace_pressure + cdef object nodes_kdtree + cdef int min_nb_steps + cdef double dt_fluid + cdef double dt_fluid_last + cdef double dt_fluid_next + cdef double dt + cdef double dt_last + cdef double t + cdef public: + bool build_kdtree + bool parallel_mode + int chrono_processor + bool first_step + string scheme # coupling scheme + string prediction # force for prediction + int step_nb # number of steps + int step_start # starting step + double sampleRate + double next_sample + bool record_values + + def __cinit__(self, np.ndarray gravity, int nd=3, dt_init=0., sampleRate=0): + self.thisptr = newSystem( gravity.data) + self.subcomponents = [] + self.dt_init = dt_init + self.model = None + self.nd = nd + self.build_kdtree = False + comm = Comm.get().comm.tompi4py() + if comm.Get_size() > 1: + parallel_mode = True + chrono_processor = 1 + else: + parallel_mode = False + chrono_processor = 0 + self.parallel_mode = parallel_mode + self.chrono_processor = chrono_processor + self.min_nb_steps = 1 # minimum number of chrono substeps + self.proteus_dt = 0. + self.first_step = True # just to know if first step + self.setCouplingScheme("CSS") + self.dt_fluid = 0 + self.dt_fluid_next = 0 + self.proteus_dt_next = 0. + self.dt = 0. + self.step_nb = 0 + self.step_start = 3 + self.sampleRate = sampleRate + self.next_sample = 0. + self.record_values = True + self.t = 0. + + def GetChTime(self): + """Gives time of Chrono system simulation + + Returns + ------- + time: double + time of chrono system + """ + time = self.thisptr.system.GetChTime() + return time + + def setCouplingScheme(self, string scheme, string prediction='backwardEuler'): + assert scheme == "CSS" or scheme == "ISS", "Coupling scheme requested unknown" + assert prediction == "backwardEuler" or prediction == "forwardEuler" or prediction == "implicitOrder2", "Prediction requested unknown" + self.scheme = scheme + self.prediction = prediction + + def attachModel(self, model, ar): + """Attaches Proteus model to auxiliary variable + """ + self.model = model + return self + + def attachAuxiliaryVariables(self,avDict): + pass + + def setMinimumSubsteps(self, int nb): + """Sets the minimum nb of chrono substeps per proteus step + if prot_dt=0.001 and ch_dt=0.002, there will be + substeps of chrono instead of just 1. + + Parameters + ---------- + nb: int + Minimum number of chrono substeps. + """ + self.min_nb_steps = nb + + def step(self, dt): + self.dt_fluid_last = self.dt_fluid + self.dt_fluid = dt + self.dt_fluid_next = self.proteus_dt_next + self.dt_last = self.dt + if self.scheme == "ISS": + self.dt = (self.dt_fluid+self.dt_fluid_last)/2. + elif self.scheme == "CSS": + self.dt = dt + # calculate number of time steps + nb_steps = max(int(dt/self.chrono_dt), 1) + if nb_steps < self.min_nb_steps: + nb_steps = self.min_nb_steps + # solve Chrono system + self.step_nb += 1 + if self.step_nb > -self.step_start: + # self.thisptr.system.setChTime() + comm = Comm.get().comm.tompi4py() + t = comm.bcast(self.thisptr.system.GetChTime(), self.chrono_processor) + Profiling.logEvent('Solving Chrono system from t=' + +str(t) + +' with dt='+str(self.dt) + +'('+str(nb_steps)+' substeps)') + if comm.rank == self.chrono_processor and dt > 0: + self.thisptr.step( self.dt, n_substeps=nb_steps) + t = comm.bcast(self.thisptr.system.GetChTime(), self.chrono_processor) + Profiling.logEvent('Solved Chrono system to t='+str(t)) + if self.scheme == "ISS": + Profiling.logEvent('Chrono system to t='+str(t+self.dt_fluid_next/2.)) + + def calculate(self, proteus_dt=None): + """Does chrono system calculation for a Proteus time step + Calls prestep and poststep on all subcomponents (bodies, + moorings, etc) attached to the system. + + Parameters + ---------- + proteus_dt: Optional[proteus_dt] + Manually sets a time step. The time step is set + automatically when coupled with a Proteus simulation + """ + self.proteus_dt_last = self.proteus_dt + if self.model is not None: + try: + self.proteus_dt = self.model.levelModelList[-1].dt_last + self.proteus_dt_next = self.model.levelModelList[-1].dt_last # BAD PREDICTION + self.t = t = self.model.stepController.t_model_last + except: + self.proteus_dt = self.dt_init + self.t = t = 0. + elif proteus_dt is not None: + self.proteus_dt = proteus_dt + self.proteus_dt_next = proteus_dt # BAD PREDICTION IF TIME STEP NOT REGULAR + self.t = t = self.thisptr.system.GetChTime() + else: + sys.exit('no time step set') + if self.model is not None and self.build_kdtree is True: + self.nodes_kdtree = spatial.cKDTree(self.model.levelModelList[-1].mesh.nodeArray) + if t >= self.next_sample: + self.record_values = True + self.next_sample += self.sampleRate + Profiling.logEvent("Chrono prestep") + for s in self.subcomponents: + s.prestep() + self.step(self.proteus_dt) + Profiling.logEvent("Chrono poststep") + for s in self.subcomponents: + s.poststep() + self.record_values = False + self.first_step = False # first step passed + + def addBodyEasyBox(self, pych.ChBodyEasyBox body): + """Hack to add BodyEasyBox + """ + self.thisptr.system.AddBody(body.sharedptr_chbody) + + def calculate_init(self): + """Does chrono system initialisation + (!) Must be called before the first calculate() call. + Calls calculate_init and poststep on all subcomponents + (bodies, moorings, etc) attached to the system. + """ + Profiling.logEvent("Starting init"+str(self.next_sample)) + self.directory = str(Profiling.logDir)+'/' + self.thisptr.setDirectory(self.directory) + if self.model is not None and self.build_kdtree is True: + self.u = self.model.levelModelList[-1].u + # finite element space (! linear for p, quadratic for velocity) + self.femSpace_velocity = self.u[1].femSpace + self.femSpace_pressure = self.u[0].femSpace + self.nodes_kdtree = spatial.cKDTree(self.model.levelModelList[-1].mesh.nodeArray) + for s in self.subcomponents: + s.calculate_init() + Profiling.logEvent("Setup initial"+str(self.next_sample)) + self.thisptr.system.SetupInitial() + Profiling.logEvent("Finished init"+str(self.next_sample)) + for s in self.subcomponents: + s.poststep() + + def setTimestepperType(self, string tstype, bool verbose=False): + """Change timestepper (default: Euler) + + Parameters + ---------- + tstype: str + type of timestepper ('Euler' or 'HHT') + """ + tstypes = ["Euler", "HHT", "Trapezoidal"] + assert str(tstype) in tstypes, str(tstype)+" not a valid choice." + self.thisptr.setTimestepperType(tstype, verbose) + + def setTimeStep(self, double dt): + """Sets time step for Chrono solver. + Calculations in Chrono will use this time step within the + Proteus time step (if bigger) + + Parameters + ---------- + dt: float + Chrono time step size + """ + self.chrono_dt = dt + self.thisptr.setChTimeStep(dt) + + def setGravity(self, np.ndarray gravity): + """Sets gravity acceleration of the Chrono system + + Parameters + ---------- + gravity: array_like + Gravity acceleration (array of length 3) + """ + self.thisptr.setGravity( gravity.data) + + def addSubcomponent(self, subcomponent): + """Adds subcomponent to system + calculate_init() of subcomponent called before initial timestep + prestep() and poststep of subcomponent() called at all timestep + + Parameters + ---------- + subcomponent: class instance + class instance of subcomponent + """ + self.subcomponents += [subcomponent] + + def recordBodyList(self): + comm = Comm.get().comm.tompi4py() + if self.parallel_mode is True: + if comm.rank == self.chrono_processor: + self.thisptr.recordBodyList() + else: + if comm.rank == 0: + self.thisptr.recordBodyList() + + def findElementContainingCoords(self, coords): + """ + Parameters + ---------- + coords: array_like + global coordinates to look for + + Returns + ------- + xi: + local coordinates + eN: int + (local) element number + rank: int + processor rank containing element + """ + #log Profiling.logEvent("Looking for element " +str(coords)) + comm = Comm.get().comm.tompi4py() + xi = owning_proc = element = rank = None # initialised as None + # get nearest node on each processor + comm.barrier() + #log Profiling.logEvent("get nearest node " +str(coords)) + nearest_node, nearest_node_distance = getLocalNearestNode(coords, self.nodes_kdtree) + # look for element containing coords on each processor (if it exists) + comm.barrier() + #log Profiling.logEvent("get local element " +str(coords)) + local_element = getLocalElement(self.femSpace_velocity, coords, nearest_node) + comm.barrier() + #log Profiling.logEvent("got local element " +str(coords)) + if local_element: + xi = self.femSpace_velocity.elementMaps.getInverseValue(local_element, coords) + # check which processor has element (if any) + # if local_element: + # print("Local element!") + # owning_proc = comm.bcast(comm.rank, comm.rank) + # # get local coords + # Profiling.logEvent("getting xi" +str(coords)) + # xi = self.femSpace_velocity.elementMaps.getInverseValue(local_element, coords) + # Profiling.logEvent("broadcasting results" +str(coords)) + # xi = comm.bcast(xi, owning_proc) + # Profiling.logEvent("Broadcasted xi") + # element = comm.bcast(local_element, owning_proc) + # Profiling.logEvent("Broadcasted element") + # rank = comm.bcast(owning_proc, owning_proc) + # Profiling.logEvent("Broadcasted rank") + # Profiling.logEvent("broadcasting results" +str(coords)) + comm.barrier() + #print("MYRANK ", comm.rank) + #log Profiling.logEvent("Starting all reduce") + global_have_element, owning_proc = comm.allreduce((local_element, comm.rank), + op=MPI.MAXLOC) + comm.barrier() + #log Profiling.logEvent("Finished all reduce") + if global_have_element: + # Profiling.logEvent("broadcasting results" +str(coords)) + xi = comm.bcast(xi, owning_proc) + #log Profiling.logEvent("Broadcasted xi" +str(xi)) + element = comm.bcast(local_element, owning_proc) + #log Profiling.logEvent("Broadcasted element" +str(element)) + rank = comm.bcast(owning_proc, owning_proc) + #log Profiling.logEvent("Broadcasted owning_proc" +str(rank)) + #log Profiling.logEvent("got element finished" +str(coords)) + return xi, element, rank + + def getFluidVelocityLocalCoords(self, xi, element, rank): + """ + Parameters + ---------- + xi: + local coords in element + element: int + element number (local to processor 'rank') + rank: int + rank of processor owning the element + """ + #log Profiling.logEvent("FINDING VELOCITY AT RANK "+str(rank)+", "+str(element) + ", " + str(xi)) + comm = Comm.get().comm.tompi4py() + if comm.rank == rank: + u = self.u[1].getValue(element, xi) + v = self.u[2].getValue(element, xi) + if self.nd > 2: + w = self.u[3].getValue(element, xi) + if self.nd <= 2: + w = 0 + # broadcast to all processors + else: + u = v = w = None + u = comm.bcast(u, rank) + v = comm.bcast(v, rank) + w = comm.bcast(w, rank) + #log Profiling.logEvent("FOUND VELOCITY") + return u, v, w + + def getFluidVelocityGradientLocalCoords(self, xi, element, rank): + comm = Comm.get().comm.tompi4py() + if comm.rank == rank: + u_grad = self.u_grad[1].getGradientValue(element, xi) + u_grad = comm.bcast(u_grad, rank) + v_grad = self.u[2].getGradientValue(element, xi) + v_grad = comm.bcast(v_grad, rank) + if self.nd > 2: + w_grad = self.u[3].getGradientValue(element, xi) + if self.nd <= 2: + w_grad = 0 + # broadcast to all processors + w_grad = comm.bcast(w_grad, rank) + return u_grad, v_grad, w_grad + + # def findFluidVelocityAtCoords(self, coords): + # """Finds solution from NS for velocity of fluid at given coordinates + + # Parameters + # ---------- + # coords: array_like + # coordinates at which velocity solution is sought + + # Returns + # ------- + # u: float + # velocity in the x direction + # v: float + # velocity in the y direction + # w: float + # velocity in the z direction (0 if 2D) + # """ + # comm = Comm.get().comm.tompi4py() + # # get nearest node on each processor + # nearest_node, nearest_node_distance = getLocalNearestNode(coords, self.nodes_kdtree) + # # look for element containing coords on each processor (if it exists) + # local_element = getLocalElement(self.femSpace_velocity, coords, nearest_node) + # # check which processor has element (if any) + # haveElement = int(local_element is not None) + # if haveElement: + # owning_proc = comm.rank + # if local_element: + # # NEXT LINE TO CHANGE + # nd = self.nd + # # get local coords + # xi = self.femSpace_velocity.elementMaps.getInverseValue(local_element, coords) + # # get solution + # u = self.u[1].getValue(local_element, xi) + # v = self.u[2].getValue(local_element, xi) + # if nd > 2: + # w = self.u[3].getValue(local_element, xi) + # # broadcast to all processors + # u = comm.bcast(u, owning_proc) + # v = comm.bcast(v, owning_proc) + # if nd > 2: + # w = comm.bcast(w, owning_proc) + # if nd <= 2: + # w = 0 + # else: + # sys.exit('{coords} outside of domain'.format(coords=str(coords))) + # return u, v, w + +# ctypedef np.ndarray vecarray(ChVector) + +# ctypedef np.ndarray (*ChVector_to_npArray) (ChVector) + +#def testx(): +# cdef ChSystem system = ChSystem() +# cdef ChBody bod = ChBody() +# cdef ChVector oo = ChVector[double](2.,3.,4.) +# bod.SetPos_dt(oo) +# cdef ChVector& gg = bod.GetPos_dt() +# print(gg.x, gg.y, gg.z) + + +cdef class Mesh: + cdef cppMesh * thisptr + def __cinit__(self, ProtChSystem system): + cdef shared_ptr[ch.ChMesh] mesh = make_shared[ch.ChMesh]() + self.thisptr = newMesh(system.thisptr.system, mesh) + def setAutomaticGravity(self, bool val): + self.thisptr.SetAutomaticGravity(val) + +cdef class SurfaceBoxNodesCloud: + cdef cppSurfaceBoxNodesCloud * thisptr + def __cinit__(self, ProtChSystem system, Mesh mesh, np.ndarray position, np.ndarray dimensions): + cdef ch.ChVector[double] pos = ch.ChVector[double](position[0], position[1], position[2]) + cdef ch.ChVector[double] dim = ch.ChVector[double](dimensions[0], dimensions[1], dimensions[2]) + self.thisptr = newSurfaceBoxNodesCloud(system.thisptr.system, + mesh.thisptr.mesh, + pos, + dim) + # self.System.addBody(self) + def setNodesSize(self, double size): + self.thisptr.setNodesSize(size) + +# def linkMoorings(System system, Moorings mooringA, int nodeA_ind, Moorings mooringB, int nodeB_ind): +# """ +# Parameters +# ---------- +# system: System +# class instance of the System where to add link +# mooringA: Moorings +# class instance of first mooring cable (A) +# nodeA_ind: int +# index of node to be used as link on mooring cable A +# mooringB: Moorings +# class instance of second mooring cable (B) +# nodeB_ind: int +# index of node to be used as link on mooring cable B +# """ +# cdef shared_ptr[ch.ChLinkPointPoint] link = make_shared[ch.ChLinkPointPoint]() +# deref(link).Initialize( mooringA.thisptr.nodes[nodeA_ind], mooringB.thisptr.nodes[nodeB_ind]) +# system.thisptr.system.Add( link) + + +cdef class ProtChMoorings: + """Class for building mooring cables + + Parameters + ---------- + system: System + Class instance of the system. + mesh: Mesh + Class instance of the mesh. + length: np.ndarray + Length of cable segments. Must be an array, if the cable only + has one type of segment (e.g. only one type of chain), an + array of length 1 can be passed. + nb_elems: np.ndarray + Number of elements per segments. + d: np.ndarray + Diameter of segments. + rho: np.ndarray + Density of segments. + E: np.ndarray + Young's modulus of segments. + beam_type: str + Type of elements (default: "CableANCF"). + """ + cdef cppMultiSegmentedCable * thisptr + cdef public: + # vector[pyfea.ChNodeFEAxyzD] nodes + # vector[pyfea.ChElementCableANCF] elems + # vector[ProtChCable] cables + str record_file + object model + ProtChSystem ProtChSystem + object Mesh + int nd + object nodes_function + object fluid_velocity_function + ProtChBody body_front + ProtChBody body_back + bool front_body + bool back_body + bool nodes_built + bool external_forces_from_ns + bool external_forces_manual + np.ndarray fluid_density_array + np.ndarray fluid_velocity_array + np.ndarray fluid_velocity_array_previous + np.ndarray fluid_acceleration_array + string name + string beam_type + int nodes_nb # number of nodes + np.ndarray nb_elems + def __cinit__(self, + ProtChSystem system, + Mesh mesh, + double[:] length, + np.ndarray nb_elems, + double[:] d, + double[:] rho, + double[:] E, + string beam_type="CableANCF"): + check_arrays = [len(length), len(nb_elems), len(d), len(rho), len(E)] + assert all(v == len(length) for v in check_arrays), 'arrays are not of same length' + self.ProtChSystem = system + self.ProtChSystem.addSubcomponent(self) + self.nd = self.ProtChSystem.nd + self.Mesh = mesh + self.beam_type = beam_type + self.nb_elems = nb_elems + cdef vector[double] vec_length + cdef vector[int] vec_nb_elems + cdef vector[double] vec_d + cdef vector[double] vec_rho + cdef vector[double] vec_E + for i in range(len(length)): + vec_length.push_back(length[i]) + vec_nb_elems.push_back(nb_elems[i]) + vec_d.push_back(d[i]) + vec_rho.push_back(rho[i]) + vec_E.push_back(E[i]) + self.thisptr = newMoorings(system.thisptr.system, + mesh.thisptr.mesh, + vec_length, + vec_nb_elems, + vec_d, + vec_rho, + vec_E, + beam_type + ) + self.nodes_function = lambda s: (s, s, s) + self.nodes_built = False + self.name = 'record_moorings' + self.external_forces_from_ns = False + self.external_forces_manual = False + + def setName(self, string name): + """Sets name of cable, used for csv file + + Parameters + ---------- + name: str + Name of cable. + """ + self.name = name + + def _recordValues(self): + """Records values in csv files + """ + t_chrono = self.ProtChSystem.thisptr.system.GetChTime() + if self.ProtChSystem.model is not None: + t_last = self.ProtChSystem.model.stepController.t_model_last + try: + dt_last = self.ProtChSystem.model.levelModelList[-1].dt_last + except: + dt_last = 0 + t = t_last + else: + t = t_chrono + t_sim = Profiling.time()-Profiling.startTime + # Positions + self.record_file = os.path.join(Profiling.logDir, self.name+'_pos.csv') + if t == 0: + headers = ['t', 't_ch', 't_sim'] + for i in range(self.thisptr.nodes.size()): + headers += ['x'+str(i), 'y'+str(i), 'z'+str(i)] + with open(self.record_file, 'w') as csvfile: + writer = csv.writer(csvfile, delimiter=',') + writer.writerow(headers) + row = [t, t_chrono, t_sim] + positions = self.getNodesPosition() + for pos in positions: + row += [pos[0], pos[1], pos[2]] + with open(self.record_file, 'a') as csvfile: + writer = csv.writer(csvfile, delimiter=',') + writer.writerow(row) + # Fairlead / anchor tensions + self.record_file = os.path.join(Profiling.logDir, self.name+'_T.csv') + if t == 0: + headers = ['t', 't_ch', 't_sim'] + # for i in range(self.thisptr.nodes.size()): + # headers += ['Tbx'+str(i), 'Tby'+str(i), 'Tbz'+str(i), 'Tfx'+str(i), 'Tfy'+str(i), 'Tfz'+str(i)] + headers += ['Tb0', 'Tb1', 'Tb2', 'Tf0', 'Tf1', 'Tf2'] + with open(self.record_file, 'w') as csvfile: + writer = csv.writer(csvfile, delimiter=',') + writer.writerow(headers) + row = [t, t_chrono, t_sim] + Tb = self.getTensionBack() + Tf = self.getTensionFront() + row += [Tb[0], Tb[1], Tb[2], Tf[0], Tf[1], Tf[2]] + with open(self.record_file, 'a') as csvfile: + writer = csv.writer(csvfile, delimiter=',') + writer.writerow(row) + # Tensions + self.record_file = os.path.join(Profiling.logDir, self.name+'_TT.csv') + if t == 0: + headers = ['t', 't_ch', 't_sim'] + for i in range(self.thisptr.nodes.size()-1): + headers += ['Tx'+str(i), 'Ty'+str(i), 'Tz'+str(i)] + with open(self.record_file, 'w') as csvfile: + writer = csv.writer(csvfile, delimiter=',') + writer.writerow(headers) + if t > 0: + row = [t, t_chrono, t_sim] + tensions = self.getNodesTension() + for T in tensions: + row += [T[0], T[1], T[2]] + with open(self.record_file, 'a') as csvfile: + writer = csv.writer(csvfile, delimiter=',') + writer.writerow(row) + # Drag + self.record_file = os.path.join(Profiling.logDir, self.name+'_drag.csv') + if t == 0: + headers = ['t', 't_ch', 't_sim'] + for i in range(self.thisptr.nodes.size()-1): + headers += ['Fx'+str(i), 'Fy'+str(i), 'Fz'+str(i)] + with open(self.record_file, 'w') as csvfile: + writer = csv.writer(csvfile, delimiter=',') + writer.writerow(headers) + if t > 0: + row = [t, t_chrono, t_sim] + forces = self.getDragForces() + for F in forces: + row += [F[0], F[1], F[2]] + with open(self.record_file, 'a') as csvfile: + writer = csv.writer(csvfile, delimiter=',') + writer.writerow(row) + + def getTensionBack(self): + """ + """ + cdef ch.ChVector T + if self.thisptr.constraint_back: + T = deref(self.thisptr.constraint_back).GetReactionOnNode() + return pych.ChVector_to_npArray(T) + else: + return np.zeros(3) + + def getTensionFront(self): + """ + """ + cdef ch.ChVector T + if self.thisptr.constraint_front: + T = deref(self.thisptr.constraint_front).GetReactionOnNode() + return pych.ChVector_to_npArray(T) + else: + return np.zeros(3) + + def calculate_init(self): + # build position vector of nodes (for each segment) + # self.setNodesPosition() + # build cable (nodes, elements, etc) + self.thisptr.buildCable() + if self.beam_type == "BeamEuler": + nb_nodes = self.thisptr.nodesRot.size() + else: + nb_nodes = self.thisptr.nodes.size() + if self.fluid_velocity_array is None: + self.fluid_velocity_array = np.zeros((nb_nodes, 3)) + self.fluid_velocity_array_previous = np.zeros((nb_nodes, 3)) + if self.fluid_acceleration_array is None: + self.fluid_acceleration_array = np.zeros((nb_nodes, 3)) + if self.fluid_density_array is None: + self.fluid_density_array = np.zeros(nb_nodes) + + + def prestep(self): + """Sets external forces on the cable (if any) + """ + if self.ProtChSystem.model is not None and self.external_forces_from_ns is True: + Profiling.logEvent('moorings extern forces') + self.setExternalForces() + elif self.external_forces_manual is True: + Profiling.logEvent('moorings manual extern forces') + self.setExternalForces() + + def poststep(self): + """Records values + """ + comm = Comm.get().comm.tompi4py() + if comm.rank == self.ProtChSystem.chrono_processor and self.ProtChSystem.record_values is True: + self._recordValues() + + def setNodesPositionFunction(self, function): + """Function to build nodes + + Parameters + ---------- + function: + Must be a function taking one argument (e.g. distance + along cable) and returning 3 arguments (x, y, z) coords. + """ + self.nodes_function = function + + def setFluidVelocityFunction(self, function): + """Function to build nodes + + Parameters + ---------- + function: + Must be a function taking two arguments (3D coordinates + and time), and returning velocity (x, y, z). + """ + self.fluid_velocity_function = function + + def fixFrontNode(self, bool fixed): + """Fix front node of cable + + Parameters + ---------- + fixed: bool + Fixes node if True + """ + assert self.nodes_built is True, 'call buildNodes() before calling this function' + if self.beam_type == "BeamEuler": + deref(self.thisptr.nodesRot.front()).SetFixed(fixed) + else: + deref(self.thisptr.nodes.front()).SetFixed(fixed) + + def fixBackNode(self, bool fixed): + """Fix back node of cable + + Parameters + ---------- + fixed: bool + Fixes node if True + """ + assert self.nodes_built is True, 'call buildNodes() before calling this function' + if self.beam_type == "BeamEuler": + deref(self.thisptr.nodesRot.back()).SetFixed(fixed) + else: + deref(self.thisptr.nodes.back()).SetFixed(fixed) + + def attachBackNodeToBody(self, ProtChBody body): + """Attaches back node to a body with ChLinkLockLock + + Parameters + ---------- + body: ProtChBody + body to which the node will be attached + """ + assert self.nodes_built is True, 'call buildNodes() before calling this function' + self.thisptr.attachBackNodeToBody(body.thisptr.body) + + def attachFrontNodeToBody(self, ProtChBody body): + """Attaches front node to a body with ChLinkLockLock + + Parameters + ---------- + body: ProtChBody + body to which the node will be attached + """ + assert self.nodes_built is True, 'call buildNodes() before calling this function' + self.thisptr.attachFrontNodeToBody(body.thisptr.body) + + def getElementMass(self, int i=0): + mass = deref(self.thisptr.elems[i]).GetMass() + return mass + + def getTensionElement(self, int i=0): + cdef ch.ChVector[double] F + F = self.thisptr.getTensionElement(i) + return np.array([F.x(), F.y(), F.z()]) + + def getNodesTension(self): + cdef ch.ChVector[double] vec + if self.beam_type == 'BeamEuler': + T = np.zeros((self.thisptr.nodesRot.size()-1,3 )) + else: + T = np.zeros(( self.thisptr.nodes.size()-1,3 )) + for i in range(np.sum(self.nb_elems)): + vec = self.thisptr.getTensionElement(i) + T[i] = [vec.x(), vec.y(), vec.z()] + return T + + def getLengthElems(self): + lengths = np.zeros(self.thisptr.elems.size()) + for i in range(self.thisptr.elems.size()): + lengths[i] = deref(self.thisptr.elems[i]).GetLengthX() + return lengths + + def setDragCoefficients(self, double tangential, double normal, int segment_nb): + """Sets drag coefficients of cable + + Parameters + ---------- + tangential: double + Tangential drag coefficient. + normal: double + Normal drag coefficient. + segment_nb: int + Segment number to which these coefficients apply. + """ + deref(self.thisptr.cables[segment_nb]).setDragCoefficients(tangential, normal) + + def setAddedMassCoefficients(self, double tangential, double normal, int segment_nb): + """Sets added mass coefficients of cable + + Parameters + ---------- + tangential: double + Tangential added mass coefficient. + normal: double + Normal added mass coefficient. + segment_nb: int + Segment number to which these coefficients apply. + """ + deref(self.thisptr.cables[segment_nb]).setAddedMassCoefficients(tangential, normal) + + def setNodesPosition(self): + """Builds the nodes of the cable. + + (!) Must be called after setNodesPositionFunction() + """ + cdef ch.ChVector[double] vec + for i in range(self.thisptr.cables.size()): + deref(self.thisptr.cables[i]).mvecs.clear() + L0 = deref(self.thisptr.cables[i]).L0 + L = deref(self.thisptr.cables[i]).length + nb_elems = deref(self.thisptr.cables[i]).nb_elems + if self.beam_type == "BeamANCF": + nb_nodes = nb_elems*2+1 + elif self.beam_type == "CableANCF" or self.beam_type == "BeamEuler": + nb_nodes = nb_elems+1 + else: + print("set element type") + sys.exit() + ds = L/(nb_nodes-1) + for j in range(nb_nodes): + x, y, z = self.nodes_function(L0+ds*j) + vec = ch.ChVector[double](x, y, z) + deref(self.thisptr.cables[i]).mvecs.push_back(vec) + self.buildNodes() + + def buildNodes(self): + self.thisptr.buildNodes() + self.nodes_built = True + if self.beam_type == "BeamEuler": + self.nodes_nb = self.thisptr.nodesRot.size() + elif self.beam_type == "CableANCF" or self.beam_type == "BeamANCF": + self.nodes_nb = self.thisptr.nodes.size() + else: + print("set element type") + sys.exit() + + def getNodesPosition(self): + """Gives array of nodes position + + Returns + ------- + pos: np.ndarray + Array of nodes position. + """ + if self.beam_type == 'BeamEuler': + pos = np.zeros(( self.thisptr.nodesRot.size(),3 )) + for i in range(self.thisptr.nodesRot.size()): + vec = deref(self.thisptr.nodesRot[i]).GetPos() + pos[i] = [vec.x(), vec.y(), vec.z()] + return pos + else: + pos = np.zeros(( self.thisptr.nodes.size(),3 )) + for i in range(self.thisptr.nodes.size()): + vec = deref(self.thisptr.nodes[i]).GetPos() + pos[i] = [vec.x(), vec.y(), vec.z()] + return pos + + def getDragForces(self): + cdef ch.ChVector Fd + drag = np.zeros(( self.thisptr.nodes.size(),3 )) + for i in range(self.thisptr.forces_drag.size()): + Fd = self.thisptr.forces_drag[i] + drag[i] = [Fd.x(), Fd.y(), Fd.z()] + return drag + + def setIyy(self, double Iyy, int cable_nb): + deref(self.thisptr.cables[cable_nb]).setIyy(Iyy) + + + def getNodesD(self): + """Gives direction of nodes + + Returns + ------- + dire: np.ndarray + Array of nodes direction. + """ + dire = np.zeros(( self.thisptr.nodes.size(),3 )) + for i in range(self.thisptr.nodes.size()): + vec = deref(self.thisptr.nodes[i]).GetD() + dire[i] = [vec.x(), vec.y(), vec.z()] + return dire + + def getNodesDD(self): + """(!) Only for BeamANCF + """ + pos = np.zeros(( self.thisptr.nodes.size(),3 )) + for i in range(self.thisptr.nodes.size()): + vec = deref(self.thisptr.nodes[i]).GetDD() + pos[i] = [vec.x(), vec.y(), vec.z()] + return pos + + def setContactMaterial(self, pych.ChMaterialSurfaceSMC mat): + """Sets contact material of the cable + + Parameters + ---------- + mat: ChMaterialSurfaceSMC + Material of cable. + """ + self.thisptr.setContactMaterial(mat.sharedptr) + + def setExternalForces(self, fluid_velocity_array=None, fluid_density_array=None, + fluid_acceleration_array=None): + """ + Sets external forces acting on cables + Pass fluid velocity_array as argument only for debugging (must be an array as long as the number of nodes) + """ + # get velocity at nodes + # cdef np.ndarray fluid_velocity = np.zeros((len(self.thisptr.nodes.size()), 3)) + self.fluid_velocity_array_previous[:] = self.fluid_velocity_array + if fluid_velocity_array is not None: + self.fluid_velocity_array = fluid_velocity_array + if fluid_density_array is not None: + self.fluid_density_array = fluid_density_array + if fluid_acceleration_array is not None: + self.fluid_acceleration_array = fluid_acceleration_array + cdef vector[ch.ChVector[double]] fluid_velocity + cdef vector[ch.ChVector[double]] fluid_acceleration + cdef ch.ChVector[double] vel + cdef ch.ChVector[double] acc + cdef vector[double] fluid_density + cdef double dens + comm = Comm.get().comm.tompi4py() + # Profiling.logEvent("STARTING LOOP ") + if self.beam_type == "BeamEuler": + nb_nodes = self.thisptr.nodesRot.size() + else: + nb_nodes = self.thisptr.nodes.size() + for i in range(nb_nodes): + if self.beam_type == "BeamEuler": + vec = deref(self.thisptr.nodesRot[i]).GetPos() + else: + vec = deref(self.thisptr.nodes[i]).GetPos() + x = vec.x() + y = vec.y() + z = vec.z() + if self.ProtChSystem.parallel_mode is True: + x = comm.bcast(x, self.ProtChSystem.chrono_processor) + y = comm.bcast(y, self.ProtChSystem.chrono_processor) + z = comm.bcast(z, self.ProtChSystem.chrono_processor) + coords = np.array([x, y, z]) + if self.ProtChSystem.model is not None and self.external_forces_from_ns is True: + vel_arr = np.zeros(3) + vel_grad_arr = np.zeros(3) + xi, el, rank = self.ProtChSystem.findElementContainingCoords(coords[:self.nd]) + # print("NODE ", i, xi, el, rank) + #log Profiling.logEvent("Got ELEMENT") + comm.barrier() + if rank is not None: + vel_arr[:] = self.ProtChSystem.getFluidVelocityLocalCoords(xi, el, rank) + else: # means node is outside domain + if self.fluid_velocity_function is not None: + self.fluid_velocity_function(coords, self.ProtChSystem.t) + vel_arr[:] = 0. + # print("VEL ", i, vel_arr) + comm.barrier() + #log Profiling.logEvent("Got VELOCITY") + #vel_grad_arr[:] = self.ProtChSystem.getFluidVelocityGradientLocalCoords(xi, el, rank) + # acc = du/dt+u.grad(u) + #acc_arr = (vel_arr-fluid_velocity_array_previous[i])/dt+vel_arr*vel_grad_arr + #arr[:self.nd] = self.ProtChSystem.findFluidVelocityAtCoords(coords[:self.nd]) + self.fluid_velocity_array[i] = vel_arr + vel = ch.ChVector[double](vel_arr[0], vel_arr[1], vel_arr[2]) + fluid_velocity.push_back(vel) + else: + if self.fluid_velocity_function is not None and fluid_velocity_array is None and False: + vel_arr = self.fluid_velocity_function(coords, self.ProtChSystem.t) + vel = ch.ChVector[double](vel_arr[0], vel_arr[1], vel_arr[2]) + else: + vel = ch.ChVector[double](self.fluid_velocity_array[i][0], self.fluid_velocity_array[i][1], self.fluid_velocity_array[i][2]) + fluid_velocity.push_back(vel) + acc = ch.ChVector[double](self.fluid_acceleration_array[i][0], self.fluid_acceleration_array[i][1], self.fluid_acceleration_array[i][2]) + fluid_acceleration.push_back(acc) + dens = self.fluid_density_array[i] + fluid_density.push_back(dens) + # Profiling.logEvent("FINISHED LOOP "+str(i)) + self.thisptr.setFluidAccelerationAtNodes(fluid_acceleration) + self.thisptr.setFluidVelocityAtNodes(fluid_velocity) + self.thisptr.setFluidDensityAtNodes(fluid_density) + # update drag forces + self.thisptr.updateDragForces() + self.thisptr.updateAddedMassForces() + self.thisptr.applyForces() + # update buoyancy forces + # self.thisptr.updateBuoyancyForces() + # update added mass forces + # self.thisptr.updateAddedMassForces() + + def setFluidDensityAtNodes(self, np.ndarray density_array): + cdef vector[double] fluid_density + self.fluid_density_array = density_array + cdef double dens + for d in density_array: + fluid_density.push_back(d) + self.thisptr.setFluidDensityAtNodes(fluid_density) + + def setFluidVelocityAtNodes(self, np.ndarray velocity_array): + cdef vector[ch.ChVector[double]] fluid_velocity + cdef ch.ChVector[double] vel + self.fluid_velocity_array = velocity_array + for v in velocity_array: + vel = ch.ChVector[double](v[0], v[1], v[2]) + fluid_velocity.push_back(vel) + self.thisptr.setFluidVelocityAtNodes(fluid_velocity) + + def setFluidAccelerationAtNodes(self, np.ndarray acceleration_array): + cdef vector[ch.ChVector[double]] fluid_acceleration + cdef ch.ChVector[double] acc + self.fluid_acceleration_array = acceleration_array + for a in acceleration_array: + acc = ch.ChVector[double](a[0], a[1], a[2]) + fluid_acceleration.push_back(acc) + self.thisptr.setFluidAccelerationAtNodes(fluid_acceleration) + + +def getLocalNearestNode(coords, kdtree): + """Finds nearest node to coordinates (local) + Parameters + ---------- + coords: array_like + coordinates from which to find nearest node + kdtree: scipy.spatial.cKDTree + instance of scipy kdtree + + Returns + ------- + node: int + nearest node index + distance: float + distance to nearest node + """ + # determine local nearest node distance + distance, node = kdtree.query(coords) + return node, distance + +def getLocalElement(femSpace, coords, node): + """Given coordinates and its nearest node, determine if it is on a + local element. + + Parameters + ---------- + femSpace: object + finite element space + coords: array_like + coordinates from which to element + node: int + nearest node index + + Returns + ------- + eN: int or None + local index of element (None if not found) + """ + patchBoundaryNodes=set() + checkedElements=[] + # nodeElementOffsets give the indices to get the elements sharing the node + #log Profiling.logEvent("Getting Local Element") + if node+1 < len(femSpace.mesh.nodeElementOffsets): + for eOffset in range(femSpace.mesh.nodeElementOffsets[node], femSpace.mesh.nodeElementOffsets[node + 1]): + eN = femSpace.mesh.nodeElementsArray[eOffset] + checkedElements.append(eN) + # union of set + patchBoundaryNodes|=set(femSpace.mesh.elementNodesArray[eN]) + # evaluate the inverse map for element eN (global to local) + xi = femSpace.elementMaps.getInverseValue(eN, coords) + #J = femSpace.elementMaps.getJacobianValues(eN, ) + # query whether xi lies within the reference element + if femSpace.elementMaps.referenceElement.onElement(xi): + return eN + else: + for eOffset in range(femSpace.mesh.nodeElementOffsets[node]): + eN = femSpace.mesh.nodeElementsArray[eOffset] + checkedElements.append(eN) + # union of set + patchBoundaryNodes|=set(femSpace.mesh.elementNodesArray[eN]) + # evaluate the inverse map for element eN (global to local) + xi = femSpace.elementMaps.getInverseValue(eN, coords) + #J = femSpace.elementMaps.getJacobianValues(eN, ) + # query whether xi lies within the reference element + if femSpace.elementMaps.referenceElement.onElement(xi): + return eN + # extra loop if case coords is in neighbour element + for node in patchBoundaryNodes: + for eOffset in range(femSpace.mesh.nodeElementOffsets[node], femSpace.mesh.nodeElementOffsets[node + 1]): + eN = femSpace.mesh.nodeElementsArray[eOffset] + if eN not in checkedElements: + checkedElements.append(eN) + # evaluate the inverse map for element eN + xi = femSpace.elementMaps.getInverseValue(eN, coords) + # query whether xi lies within the reference element + if femSpace.elementMaps.referenceElement.onElement(xi): + return eN + # no elements found + return None + diff --git a/proteus/mbd/ChronoHeaders.pxd b/proteus/mbd/ChronoHeaders.pxd new file mode 100644 index 0000000000..14c59a0131 --- /dev/null +++ b/proteus/mbd/ChronoHeaders.pxd @@ -0,0 +1,238 @@ + +from libcpp.string cimport string +from libcpp.vector cimport vector +from libcpp cimport bool +from libcpp.memory cimport (shared_ptr, + make_shared) + +cdef extern from "ChMoorings.h": + + # ------- CORE ------- # + + cdef cppclass ChVector[double]: + double x() + double y() + double z() + ChVector(double x, double y, double z) + ChVector() + + cdef cppclass ChQuaternion[double]: + double e0() + double e1() + double e2() + double e3() + ChQuaternion(double e0, double e1, double e2, double e3) + ChQuaternion() + + cdef cppclass ChMatrix + + cdef cppclass ChMatrix33[double]: + ChVector Get_A_Xaxis() + ChVector Get_A_Yaxis() + ChVector Get_A_Zaxis() + + + # ------- PHYSICS ------- # + + cdef cppclass ChSystem: + void Add(shared_ptr[ChPhysicsItem] newitem) + void AddBody(shared_ptr[ChBody] newbody) + + cdef cppclass ChSystemSMC: + void Add(shared_ptr[ChPhysicsItem] newitem) + void AddBody(shared_ptr[ChBody] newbody) + double GetStep() + double GetChTime() + void SetupInitial() + + cdef cppclass ChPhysicsItem: + ChPhysicsItem() + + cdef cppclass ChFrame[double]: + void SetPos(const ChVector[double]& mpos) except + + ChVector& GetPos() + ChQuaternion& GetRot() + void SetRot(ChQuaternion &rot) except + + void SetPos(ChVector &pos) except + + ChMatrix33& GetA() + ChVector GetRotAxis() + double GetRotAngle() + + cdef cppclass ChFrameMoving[double](ChFrame): + ChVector& GetPos_dt() + ChQuaternion& GetRot_dt() + ChVector& GetPos_dtdt() + ChQuaternion& GetRot_dtdt() + ChMatrix33 GetA_dt() + ChMatrix33 GetA_dtdt() + ChVector GetWvel_loc() + ChVector GetWacc_loc() + + cdef cppclass ChBodyFrame(ChFrameMoving): + ChBodyFrame() + + cdef cppclass ChBody(ChPhysicsItem, ChBodyFrame): + ChBody() except + + # void SetRot(ChQuaternion &rot) except + + void SetInertiaXX(ChVector &iner) + void SetInertiaXY(ChVector &iner) + void SetBodyFixed(bool state) except + + void SetMaterialSurface(const shared_ptr[ChMaterialSurface] &mnewsurf) except + + void SetMass(double newmass) + double GetMass() + + cdef cppclass ChBodyEasyBox(ChBody): + ChBodyEasyBox(double Xsize, double Ysize, double Zsize, double mdensity, bool collide=False, bool visual_asset=True) + + + # ------- NODES ------- # + + cdef cppclass ChNodeBase: + ChNodeBase() + + cdef cppclass ChNodeXYZ(ChNodeBase): + void SetPos (const ChVector &mpos) + const ChVector& GetPos() + void SetPos_dt (const ChVector &mposdt) + const ChVector& GetPos_dt() + void SetPos_dtdt (const ChVector &mposdtdt) + const ChVector& GetPos_dtdt() + + cdef cppclass ChNodeFEAbase(ChNodeBase): + ChNodeFEAbase() + void Relax() + void SetNoSpeedNoAcceleration() + void SetFixed(bool mev) + bool GetFixed() + void SetIndex() + int GetIndex() + + cdef cppclass ChNodeFEAxyz(ChNodeXYZ, ChNodeFEAbase): + ChNodeFEAxyz() + double GetMass() + void SetMass(double mm) + void SetForce(ChVector mf) + ChVector& GetForce() + + cdef cppclass ChNodeFEAxyzD(ChNodeFEAxyz): + const ChVector& GetD() + + cdef cppclass ChNodeFEAxyzDD(ChNodeFEAxyzD): + const ChVector& GetDD() + + cdef cppclass ChNodeFEAxyzrot(ChNodeFEAbase, ChBodyFrame): + void SetForce(ChVector mf) + ChVector& GetForce() + void SetTorque(ChVector mf) + ChVector& GetTorque() + + cdef cppclass ChNodeFEAxyzP(ChNodeFEAbase) + + cdef cppclass ChNodeFEAcurv(ChNodeFEAbase) + + + # ------- ELEMENTS ------- # + cdef cppclass ChElementBase: + shared_ptr[ChNodeFEAbase] GetNodeN(int n) + + cdef cppclass ChElementGeneric(ChElementBase): + ChElementGeneric() + + cdef cppclass ChElementBeam(ChElementGeneric): + void EvaluateSectionDisplacement(const double eta, const ChMatrix &displ, ChVector &u_displ, ChVector &u_rotaz) + void EvaluateSectionFrame(const double eta, const ChMatrix &displ, ChVector &u_displ, ChQuaternion &rot) + void EvaluateSectionForceTorque(const double eta, const ChMatrix &displ, ChVector &Fforce, ChVector &Mtorque) + void EvaluateSectionStrain(const double eta, const ChMatrix &displ, ChVector &StrainV) + double GetMass() + double GetRestLength() + void SetRestLength(double ml) + + cdef cppclass ChElementCableANCF(ChElementBeam): + void SetNodes(shared_ptr[ChNodeFEAxyzD] nodeA, shared_ptr[ChNodeFEAxyzD] nodeB) + # void SetSection() + void SetNodes(shared_ptr[ChNodeFEAxyzD] nodeA, shared_ptr[ChNodeFEAxyzD] nodeB) + + cdef cppclass ChElementBeamANCF: + void SetNodes(shared_ptr[ChNodeFEAxyzDD] nodeA, shared_ptr[ChNodeFEAxyzDD] nodeB, shared_ptr[ChNodeFEAxyzDD] nodeC) + void SetDimensions(double lenX, double beam_h, double beam_w) + shared_ptr[ChNodeFEAxyzDD] GetNodeA() + shared_ptr[ChNodeFEAxyzDD] GetNodeB() + shared_ptr[ChNodeFEAxyzDD] GetNodeC() + void setAlphaDamp(double a) + double GetLengthX() const + double GetMass() + + # ------- SECTIONS ------- # + cdef cppclass ChBeamSection: + bool IsCircular() + void SetCircular(bool ic) + + + cdef cppclass ChBeamSectionCable(ChBeamSection): + ChBeamSectionCable() except + + void SetArea(const double ma) + double GetArea() const + void SetI(double ma) + double GetI() const + void SetDiameter(double diameter) + void SetDensity(double md) + double GetDensity() const + void SetYoungModulus(double mE) + double GetYoungModulus() const + void SetBeamRaleyghDamping(double mr) + double GetBeamRaleyghDamping() + + + + cdef cppclass ChMesh: + void SetAutomaticGravity(bool mg, int num_points=1) + + cdef cppclass ChMaterialSurface: + ChMaterialSurface() except + + + cdef cppclass ChMaterialSurfaceSMC(ChMaterialSurface): + void SetYoungModulus(float val) + void SetPoissonRatio(float val) + void SetSfriction(float val) + void SetKfriction(float val) + void SetFriction(float val) + void SetRestitution(float val) + void SetAdhesion(float val) + void SetAdhesionMultDMT(float val) + void SetKn(float val) + void SetKt(float val) + void SetGn(float val) + void SetGt(float val) + double GetYoungModulus() + double GetPoissonRatio() + double GetSfriction() + double GetKfriction() + double GetRestitution() + double GetAdhesion() + double GetAdhesionMultDMT() + double GetKn() + double GetKt() + double GetGn() + double GetGt() + + cdef cppclass ChContactSurface: + ChMesh* GetMesh() + + cdef cppclass ChContactSurfaceNodeCloud(ChContactSurface): + ChContactSurfaceNodeCloud() + void AddNode(shared_ptr[ChNodeFEAxyz] mnode, const double point_radius=0.001) + void AddNode(shared_ptr[ChNodeFEAxyzrot] mnode, const double point_radius=0.001) + void AddAllNodes(const double point_radius) + + cdef cppclass ChLinkPointFrame: + int Initialize(shared_ptr[ChNodeFEAxyz] node, shared_ptr[ChBodyFrame] body, ChVector* pos) + ChVector GetReactionOnNode() + ChVector GetReactionOnBody() + #virtual + #int Initialize(shared_ptr[ChNodeFEAxyz] node, shared_ptr[ChBodyFrame] body, ChVector[double] *pos=0) + + cdef cppclass ChLinkPointPoint: + #virtual + ChLinkPointPoint() + int Initialize(shared_ptr[ChNodeFEAxyz] anodeA, shared_ptr[ChNodeFEAxyz] anodeB) + diff --git a/proteus/mbd/__init__.py b/proteus/mbd/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/proteus/mbd/pyChronoCore.pxd b/proteus/mbd/pyChronoCore.pxd new file mode 100644 index 0000000000..391daa2671 --- /dev/null +++ b/proteus/mbd/pyChronoCore.pxd @@ -0,0 +1,101 @@ +cimport numpy as np +from libcpp.string cimport string +from libcpp.vector cimport vector +from libcpp cimport bool +from libcpp.memory cimport (shared_ptr, + make_shared) +from cython.operator cimport dereference as deref +cimport ChronoHeaders as ch + +cdef class ChVector: + cdef ch.ChVector cppobj + cpdef double x(self) + cpdef double y(self) + cpdef double z(self) + +cdef class ChQuaternion: + cdef ch.ChQuaternion cppobj + cpdef double e0(self) + cpdef double e1(self) + cpdef double e2(self) + cpdef double e3(self) + +cdef class ChFrame: + cdef shared_ptr[ch.ChFrame] sharedptr_chframe + cpdef np.ndarray GetPos(self) + cpdef np.ndarray GetRot(self) + cpdef void SetRot(self, ChQuaternion rot) + cpdef void SetPos(self, ChVector mpos) + cpdef np.ndarray GetA(self) + cpdef np.ndarray GetRotAxis(self) + cpdef double GetRotAngle(self) + +cdef class ChFrameMoving(ChFrame): + cdef shared_ptr[ch.ChFrameMoving] sharedptr_chframemoving + cpdef np.ndarray GetPos_dt(self) + cpdef np.ndarray GetPos_dtdt(self) + cpdef np.ndarray GetRot_dt(self) + cpdef np.ndarray GetRot_dtdt(self) + cpdef np.ndarray GetWvel_loc(self) + cpdef np.ndarray GetWacc_loc(self) + +cdef class ChBodyFrame(ChFrameMoving): + cdef shared_ptr[ch.ChBodyFrame] sharedptr_chbodyframe + +cdef class ChBody(ChBodyFrame): + cdef shared_ptr[ch.ChBody] sharedptr_chbody + cpdef void SetBodyFixed(self, bool state) + cpdef void SetMaterialSurface(self, ChMaterialSurfaceSMC mat) + cpdef void SetInertiaXX(self, ChVector iner) + cpdef void SetInertiaXY(self, ChVector iner) + cpdef void SetMass(self, double newmass) + cpdef double GetMass(self) + +cdef class ChBodyEasyBox(ChBody): + cdef shared_ptr[ch.ChBodyEasyBox] sharedptr_chbodyeasybox + +cdef class ChMaterialSurfaceSMC: + cdef shared_ptr[ch.ChMaterialSurfaceSMC] sharedptr + cpdef void SetYoungModulus(self, float val) + cpdef void SetPoissonRatio(self, float val) + cpdef void SetSfriction(self, float val) + cpdef void SetKfriction(self, float val) + cpdef void SetFriction(self, float val) + cpdef void SetRestitution(self, float val) + cpdef void SetAdhesion(self, float val) + cpdef void SetAdhesionMultDMT(self, float val) + cpdef void SetKn(self, float val) + cpdef void SetKt(self, float val) + cpdef void SetGn(self, float val) + cpdef void SetGt(self, float val) + cpdef double GetYoungModulus(self) + cpdef double GetPoissonRatio(self) + cpdef double GetSfriction(self) + cpdef double GetKfriction(self) + cpdef double GetRestitution(self) + cpdef double GetAdhesion(self) + cpdef double GetAdhesionMultDMT(self) + cpdef double GetKn(self) + cpdef double GetKt(self) + cpdef double GetGn(self) + cpdef double GetGt(self) + + +cdef class ChContactSurfaceNodeCloud: + cdef shared_ptr[ch.ChContactSurfaceNodeCloud] sharedptr + cpdef void AddAllNodes(self, double point_radius) + + + +cdef np.ndarray ChVector_to_npArray(ch.ChVector &myvec) +cdef np.ndarray ChQuaternion_to_npArray(ch.ChQuaternion &quat) +cdef np.ndarray ChMatrix33_to_npArray(ch.ChMatrix33 &mat) +cdef ch.ChVector npArray_to_ChVector(np.ndarray arr) +cdef ch.ChQuaternion npArray_to_ChQuaternion(np.ndarray arr) + +# cdef class ChLinkPointPoint: +# cdef shared_ptr[ch.ChLinkPointPoint] sharedptr +# def __cinit__(self): +# self.sharedptr = make_shared[ch.ChLinkPointPoint]() +# def Initialize(shared_ptr[ChNodeFEAxyz] anodeA, shared_ptr[ChNodeFEAxyz]) +# deref(self.sharedptr).Initialize( anodeA, anodeB) diff --git a/proteus/mbd/pyChronoCore.pyx b/proteus/mbd/pyChronoCore.pyx new file mode 100644 index 0000000000..1fa0dce531 --- /dev/null +++ b/proteus/mbd/pyChronoCore.pyx @@ -0,0 +1,303 @@ +# distutils: language = c++ + +cimport numpy as np +import numpy as np +from libcpp.string cimport string +from libcpp.vector cimport vector +from libcpp cimport bool +from libcpp.memory cimport (shared_ptr, + make_shared) +from cython.operator cimport dereference as deref +cimport ChronoHeaders as ch + + +cdef np.ndarray ChVector_to_npArray(ch.ChVector &myvec): + return np.array([myvec.x(), myvec.y(), myvec.z()]) + +cdef np.ndarray ChQuaternion_to_npArray(ch.ChQuaternion &quat): + return np.array([quat.e0(), quat.e1(), quat.e2(), quat.e3()]) + +cdef np.ndarray ChMatrix33_to_npArray(ch.ChMatrix33 &mat): + return np.array([[mat.Get_A_Xaxis().x(), mat.Get_A_Xaxis().y(), mat.Get_A_Xaxis().z()], + [mat.Get_A_Yaxis().x(), mat.Get_A_Yaxis().y(), mat.Get_A_Yaxis().z()], + [mat.Get_A_Zaxis().x(), mat.Get_A_Zaxis().y(), mat.Get_A_Zaxis().z()]]) + +cdef ch.ChVector npArray_to_ChVector(np.ndarray arr): + cdef ch.ChVector vec + vec = ch.ChVector[double](arr[0], arr[1], arr[2]) + return vec + +cdef ch.ChQuaternion npArray_to_ChQuaternion(np.ndarray arr): + cdef ch.ChQuaternion quat + quat = ch.ChQuaternion[double](arr[0], arr[1], arr[2], arr[3]) + return quat + + +cdef class ChVector: + """Cython class for ChVector + (!) Does not use pointer + """ + + def __cinit__(self, double x, double y, double z): + self.cppobj = ch.ChVector(x, y, z) + + cpdef double x(self): + return self.cppobj.x() + + cpdef double y(self): + return self.cppobj.y() + + cpdef double z(self): + return self.cppobj.z() + + +cdef class ChQuaternion: + """Cython class for ChQuaternion + (!) Does not use pointer + """ + + def __cinit__(self, double e0, double e1, double e2, double e3): + self.cppobj = ch.ChQuaternion(e0, e1, e2, e3) + + cpdef double e0(self): + return self.cppobj.e0() + + cpdef double e1(self): + return self.cppobj.e1() + + cpdef double e2(self): + return self.cppobj.e2() + + cpdef double e3(self): + return self.cppobj.e3() + + +cdef class ChFrame: + """Cython class for ChFrame + (!) Uses shared_ptr + """ + + def __cinit(self): + if type(self) is ChFrame: + self.sharedptr_chframe = make_shared[ch.ChFrame]() + + cpdef np.ndarray GetPos(self): + return ChVector_to_npArray(deref(self.sharedptr_chframe).GetPos()) + + cpdef void SetPos(self, ChVector mpos): + deref(self.sharedptr_chframe).SetPos(mpos.cppobj) + + cpdef np.ndarray GetRot(self): + return ChQuaternion_to_npArray(deref(self.sharedptr_chframe).GetRot()) + + cpdef void SetRot(self, ChQuaternion rot): + deref(self.sharedptr_chframe).SetRot(rot.cppobj) + + cpdef np.ndarray GetA(self): + return ChMatrix33_to_npArray(deref(self.sharedptr_chframe).GetA()) + + cpdef np.ndarray GetRotAxis(self): + cdef ch.ChVector vec + vec = deref(self.sharedptr_chframe).GetRotAxis() + return ChVector_to_npArray(vec) + + cpdef double GetRotAngle(self): + return deref(self.sharedptr_chframe).GetRotAngle() + + +cdef class ChFrameMoving(ChFrame): + """Cython class for ChMovingFrame + (!) Uses shared_ptr + """ + + def __cinit(self): + if type(self) is ChFrameMoving: + self.sharedptr_chframemoving = make_shared[ch.ChFrameMoving]() + self.sharedptr_chframe = self.sharedptr_chframemoving + + cpdef np.ndarray GetPos_dt(self): + return ChVector_to_npArray(deref(self.sharedptr_chframemoving).GetPos_dt()) + + cpdef np.ndarray GetPos_dtdt(self): + return ChVector_to_npArray(deref(self.sharedptr_chframemoving).GetPos_dtdt()) + + cpdef np.ndarray GetRot_dt(self): + return ChQuaternion_to_npArray(deref(self.sharedptr_chframemoving).GetRot_dt()) + + cpdef np.ndarray GetRot_dtdt(self): + return ChQuaternion_to_npArray(deref(self.sharedptr_chframemoving).GetRot_dtdt()) + + cpdef np.ndarray GetWvel_loc(self): + cdef ch.ChVector vec + vec = deref(self.sharedptr_chframemoving).GetWvel_loc() + return ChVector_to_npArray(vec) + + cpdef np.ndarray GetWacc_loc(self): + cdef ch.ChVector vec + vec = deref(self.sharedptr_chframemoving).GetWacc_loc() + return ChVector_to_npArray(vec) + + +cdef class ChBodyFrame(ChFrameMoving): + """Cython class for ChBodyFrame + (!) Uses shared_ptr + """ + + def __cinit(self): + # following commented does not work because abstract class + pass + # if type(self) is ChBodyFrame: + # self.sharedptr_chbodyframe = make_shared[ch.ChBodyFrame]() + # self.sharedptr_chframemoving = self.sharedptr_chbodyframe + # self.sharedptr_chframe = self.sharedptr_chbodyframe + + +cdef class ChBody(ChBodyFrame): + """Cython class for ChBody + (!) Uses shared_ptr + """ + + def __cinit__(self): + if type(self) is ChBody: + self.sharedptr_chbody = make_shared[ch.ChBody]() + self.sharedptr_chbodyframe = self.sharedptr_chbody + self.sharedptr_chframemoving = self.sharedptr_chbody + self.sharedptr_chframe = self.sharedptr_chbody + + cpdef void SetBodyFixed(self, bool state): + deref(self.sharedptr_chbody).SetBodyFixed(state) + + cpdef void SetMaterialSurface(self, ChMaterialSurfaceSMC mat): + deref(self.sharedptr_chbody).SetMaterialSurface( mat.sharedptr) + + cpdef void SetInertiaXX(self, ChVector iner): + deref(self.sharedptr_chbody).SetInertiaXX(iner.cppobj) + + cpdef void SetInertiaXY(self, ChVector iner): + deref(self.sharedptr_chbody).SetInertiaXY(iner.cppobj) + + cpdef void SetMass(self, double newmass): + deref(self.sharedptr_chbody).SetMass(newmass) + + cpdef double GetMass(self): + return deref(self.sharedptr_chbody).GetMass() + + +cdef class ChBodyEasyBox(ChBody): + """Cython class for ChBodyEasyBox + (!) Uses shared_ptr + """ + + def __cinit__(self, double Xsize, double Ysize, double Zsize, double mdensity, bool collide=True, bool visual_asset=False): + if type(self) is ChBodyEasyBox: + self.sharedptr_chbodyeasybox = make_shared[ch.ChBodyEasyBox](Xsize, Ysize, Zsize, mdensity, collide, visual_asset) + self.sharedptr_chbody = self.sharedptr_chbodyeasybox + self.sharedptr_chbodyframe = self.sharedptr_chbodyeasybox + self.sharedptr_chframemoving = self.sharedptr_chbodyeasybox + self.sharedptr_chframe = self.sharedptr_chbodyeasybox + # to remove below + + +cdef class ChMaterialSurfaceSMC: + """cython class for chmaterialsurfacedem + (!) uses shared_ptr + """ + + def __cinit__(self): + self.sharedptr = make_shared[ch.ChMaterialSurfaceSMC]() + + cpdef void SetYoungModulus(self, float val): + deref(self.sharedptr).SetYoungModulus(val) + + cpdef void SetPoissonRatio(self, float val): + deref(self.sharedptr).SetPoissonRatio(val) + + cpdef void SetSfriction(self, float val): + deref(self.sharedptr).SetSfriction(val) + + cpdef void SetKfriction(self, float val): + deref(self.sharedptr).SetKfriction(val) + + cpdef void SetFriction(self, float val): + deref(self.sharedptr).SetFriction(val) + + cpdef void SetRestitution(self, float val): + deref(self.sharedptr).SetRestitution(val) + + cpdef void SetAdhesion(self, float val): + deref(self.sharedptr).SetAdhesion(val) + + cpdef void SetAdhesionMultDMT(self, float val): + deref(self.sharedptr).SetAdhesionMultDMT(val) + + cpdef void SetKn(self, float val): + deref(self.sharedptr).SetKn(val) + + cpdef void SetKt(self, float val): + deref(self.sharedptr).SetKt(val) + + cpdef void SetGn(self, float val): + deref(self.sharedptr).SetGn(val) + + cpdef void SetGt(self, float val): + deref(self.sharedptr).SetGt(val) + + cpdef double GetYoungModulus(self): + return deref(self.sharedptr).GetYoungModulus() + + cpdef double GetPoissonRatio(self): + return deref(self.sharedptr).GetPoissonRatio() + + cpdef double GetSfriction(self): + return deref(self.sharedptr).GetSfriction() + + cpdef double GetKfriction(self): + return deref(self.sharedptr).GetKfriction() + + cpdef double GetRestitution(self): + return deref(self.sharedptr).GetRestitution() + + cpdef double GetAdhesion(self): + return deref(self.sharedptr).GetAdhesion() + + cpdef double GetAdhesionMultDMT(self): + return deref(self.sharedptr).GetAdhesionMultDMT() + + cpdef double GetKn(self): + return deref(self.sharedptr).GetKn() + + cpdef double GetKt(self): + return deref(self.sharedptr).GetKt() + + cpdef double GetGn(self): + return deref(self.sharedptr).GetGn() + + cpdef double GetGt(self): + return deref(self.sharedptr).GetGt() + + +cdef class ChContactSurfaceNodeCloud: + """Cython class for ChContactSurfaceNodeCloud + (!) Uses shared_ptr + """ + + def __cinit__(self): + self.sharedptr = make_shared[ch.ChContactSurfaceNodeCloud]() + + cpdef void AddAllNodes(self, double point_radius): + deref(self.sharedptr).AddAllNodes(point_radius) + + +# cdef class ChLinkPointPoint: +# cdef shared_ptr[ch.ChLinkPointPoint] sharedptr +# def __cinit__(self): +# self.sharedptr = make_shared[ch.ChLinkPointPoint]() +# def Initialize(shared_ptr[ChNodeFEAxyz] anodeA, shared_ptr[ChNodeFEAxyz]) +# deref(self.sharedptr).Initialize( anodeA, anodeB) + + + + + + + diff --git a/proteus/mprans/BoundaryConditions.py b/proteus/mprans/BoundaryConditions.py index c7d82070c0..f2f57fec82 100644 --- a/proteus/mprans/BoundaryConditions.py +++ b/proteus/mprans/BoundaryConditions.py @@ -91,8 +91,10 @@ def setNonMaterial(self): self.v_diffusive.setConstantBC(0.) self.w_diffusive.setConstantBC(0.) - def setTank(self): - b_or = self._b_or + def setTank(self, b_or=None): + if b_or is None: + assert self._b_or is not None, 'Boundary orientation must be defined!' + b_or = self._b_or self.u_stress.uOfXT = 0. self.v_stress.uOfXT = 0. self.w_stress.uOfXT = 0. @@ -116,9 +118,9 @@ def setFixedNodes(self): self.hx_dirichlet.setConstantBC(0.) self.hy_dirichlet.setConstantBC(0.) self.hz_dirichlet.setConstantBC(0.) - self.u_stress.uOfXT = 0 - self.v_stress.uOfXT = 0 - self.w_stress.uOfXT = 0 + self.u_stress.uOfXT = 0. + self.v_stress.uOfXT = 0. + self.w_stress.uOfXT = 0. def setNoSlip(self): """ diff --git a/proteus/mprans/MCorr.h b/proteus/mprans/MCorr.h index 44ef047d90..51ef12b8eb 100644 --- a/proteus/mprans/MCorr.h +++ b/proteus/mprans/MCorr.h @@ -1909,7 +1909,7 @@ namespace proteus /* ck.calculateGScale(G,dir,h_phi); */ epsHeaviside=epsFactHeaviside*(useMetrics*h_phi+(1.0-useMetrics)*elementDiameter[eN]); q_H[eN_k] = q_porosity[eN_k]*smoothedHeaviside(epsHeaviside,q_phi[eN_k]); - }//k + }//k for (int i=0;i