From 73ccc2dd302f9702413efc69de52a32827e8e514 Mon Sep 17 00:00:00 2001
From: acrovato <a.crovato@uliege.be>
Date: Fri, 16 Sep 2022 10:53:01 +0200
Subject: [PATCH] Move CUPyDO interface to this repo

---
 dart/api/cupydo.py | 140 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 140 insertions(+)
 create mode 100644 dart/api/cupydo.py

diff --git a/dart/api/cupydo.py b/dart/api/cupydo.py
new file mode 100644
index 0000000..d781fd9
--- /dev/null
+++ b/dart/api/cupydo.py
@@ -0,0 +1,140 @@
+# -*- coding: utf-8 -*-
+
+# Copyright 2021 University of Liège
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+## CUPYDO interface
+# Adrien Crovato
+
+from cupydo.genericSolvers import FluidSolver
+import numpy as np
+import sys
+
+class Dart(FluidSolver):
+    """DART interface for CUPyDO
+    """
+    def __init__(self, _module, _nthreads):
+        # load the python module and initialize the solver
+        module = __import__(_module)
+        params = module.getParams()
+        params['Threads'] = _nthreads
+        from dart.api.core import initDart
+        _dart = initDart(params, scenario='aerostructural', task='analysis')
+        self.qinf, self.msh, self.writer, self.morpher, self.boundary, self.solver = (_dart.get(key) for key in ['qinf', 'msh', 'wrt', 'mrf', 'bnd', 'sol'])
+
+        # count fsi nodes and get their positions
+        self.nNodes = self.boundary.nodes.size()
+        self.nHaloNode = 0
+        self.nPhysicalNodes = self.nNodes - self.nHaloNode
+        self.nodalInitPosX, self.nodalInitPosY, self.nodalInitPosZ = self.getNodalInitialPositions()
+
+        # init save frequency (fsi)
+        if 'SaveFreq' in params:
+            self.saveFreq = params['SaveFreq']
+        else:
+            self.saveFreq = sys.maxsize
+
+        # generic init
+        FluidSolver.__init__(self)
+
+    def run(self, t1, t2):
+        """Run the solver for one steady (time) iteration.
+        """
+        status = self.solver.run()
+        if status > 1:
+            raise RuntimeError('DART solver diverged!\n')
+        self.__setCurrentState()
+
+    def __setCurrentState(self):
+        """Compute nodal forces from nodal normalized forces
+        """
+        i = 0
+        for n in self.boundary.nodes:
+            self.nodalLoad_X[i] = self.qinf * self.boundary.nLoads[i][0]
+            self.nodalLoad_Y[i] = self.qinf * self.boundary.nLoads[i][1]
+            self.nodalLoad_Z[i] = self.qinf * self.boundary.nLoads[i][2]
+            i += 1
+
+    def getNodalInitialPositions(self):
+        """Get the initial position of each node
+        """
+        x0 = np.zeros(self.nPhysicalNodes)
+        y0 = np.zeros(self.nPhysicalNodes)
+        z0 = np.zeros(self.nPhysicalNodes)
+        for i in range(self.boundary.nodes.size()):
+            n = self.boundary.nodes[i]
+            x0[i] = n.pos[0]
+            y0[i] = n.pos[1]
+            z0[i] = n.pos[2]
+
+        return (x0, y0, z0)
+
+    def getNodalIndex(self, iVertex):
+        """Get index of each node
+        """
+        no = self.boundary.nodes[iVertex].no
+        return no
+
+    def applyNodalDisplacements(self, dx, dy, dz, dx_nM1, dy_nM1, dz_nM1, haloNodesDisplacements, time):
+        """Apply displacements coming from solid solver to f/s interface after saving
+        """
+        self.morpher.savePos()
+        for i in range(self.boundary.nodes.size()):
+            self.boundary.nodes[i].pos[0] = self.nodalInitPosX[i] + dx[i]
+            self.boundary.nodes[i].pos[1] = self.nodalInitPosY[i] + dy[i]
+            self.boundary.nodes[i].pos[2] = self.nodalInitPosZ[i] + dz[i]
+
+    def meshUpdate(self, nt):
+        """Deform the mesh using linear elasticity equations
+        """
+        self.morpher.deform()
+
+    def save(self, nt):
+        """Save data on disk at each converged timestep
+        """
+        self.solver.save(self.writer, '_converged')
+        self.writer.save(self.msh.name + '_converged')
+
+    def initRealTimeData(self):
+        """Initialize history file
+        """
+        histFile = open('DartHistory.dat', 'w')
+        histFile.write('{0:>12s}   {1:>12s}   {2:>12s}   {3:>12s}   {4:>12s}\n'.format('Time', 'FSI_Iter', 'C_Lift', 'C_Drag', 'C_Moment'))
+        histFile.close()
+
+    def saveRealTimeData(self, time, nFSIIter):
+        """Save data at each fsi iteration
+        """
+        # history at each iteration
+        histFile = open('DartHistory.dat', 'a')
+        histFile.write('{0:12.6f}   {1:12d}   {2:12.6f}   {3:12.6f}   {4:12.6f}\n'.format(time, nFSIIter, self.solver.Cl, self.solver.Cd, self.solver.Cm))
+        histFile.close()
+        # full solution at user-defined frequency
+        if np.mod(nFSIIter+1, self.saveFreq) == 0:
+            self.solver.save(self.writer, '_{:04d}'.format(int(nFSIIter+1)//int(self.saveFreq)))
+
+    def printRealTimeData(self, time, nFSIIter):
+        """Print data on screen at the end of fsi simulation
+        """
+        print('[DART lift, drag, moment]: {0:6.3f}, {1:6.4f}, {2:6.3f}'.format(self.solver.Cl, self.solver.Cd, self.solver.Cm))
+        print('')
+
+    def exit(self):
+        """Clear memory
+        """
+        del self.boundary
+        del self.solver
+        del self.morpher
+        del self.writer
+        del self.msh
-- 
GitLab