Skip to content
Snippets Groups Projects
cupydo.py 5.04 KiB
# -*- 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:
            return False
        self.__setCurrentState()
        return True

    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