/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2020-2023 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. OpenFOAM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OpenFOAM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenFOAM. If not, see . Application building-motion Description Forced oscillation code for fluid-structure interface check. Generates position and rotation angle of each node. The top displacements of the target building calculated as sin() function. Horizontal displacements of each node interpolated to the vertical direction according to cantilever beam theory. \*---------------------------------------------------------------------------*/ #include "argList.H" #include "Time.H" #include "Fstream.H" #include "unitConversion.H" #include "foamVtkSeriesWriter.H" #include "lumpedPointTools.H" #include "lumpedPointState.H" #include "lumpedPointIOMovement.H" using namespace Foam; //- Oscillator generator class position_generator { // Private Member Functions //- Calculate position/rotation at given time lumpedPointState calc(scalar currTime) const { // Point positions pointField points_(nDivisions+1, Zero); // Point rotations vectorField angles_(nDivisions+1, Zero); // Set node heights (z) forAll(points_, divi) { points_[divi].z() = (height * divi)/scalar(nDivisions); } const vector sines ( Foam::sin(2*constant::mathematical::pi * currTime/period.x()), Foam::sin(2*constant::mathematical::pi * currTime/period.y()), Foam::sin(2*constant::mathematical::pi * currTime/period.z()) ); for (label divi = 1; divi <= nDivisions; ++divi) { const scalar zpos = points_[divi].z(); const scalar height1 = (height - zpos); const scalar pos_factor = ( 1.0/3.0 / pow4(height) * ( 3*pow4(height) - 4*pow3(height)*(height1) + pow4(height1) ) ); const scalar ang_factor = ( 1.0/3.0 / pow4(height) * ( 4*pow3(height) - 4*pow3(height1) ) ); vector here ( (amplitude.x() * sines.x() * pos_factor), (amplitude.y() * sines.y() * pos_factor), zpos // Z position is invariant ); vector rot ( Foam::atan(amplitude.x() * sines.x() * ang_factor), Foam::atan(amplitude.y() * sines.y() * ang_factor), Foam::atan(amplitude.z() * sines.z() * ang_factor) ); // Assign points_[divi] = here; // The x<->y swap is intentional angles_[divi] = vector{rot.y(), rot.x(), rot.z()}; } // Return as lumpedPoint state return lumpedPointState{points_, angles_}; } public: // Control parameters // The number of oscillating nodes label nDivisions = 10; // Height of target building [m] scalar height = 0.5; // Proper period (sec) vector period = vector{1, 0.5, 1}; // Amplitude vector amplitude = vector{0.03, 0.05, 0.3}; // Constructors //- Default construct position_generator() = default; // Member Functions //- Calculate position/rotation at given time lumpedPointState state(const scalar currTime) const { return calc(currTime); } }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // int main(int argc, char *argv[]) { argList::addNote ( "Forced oscillation code for fluid-structure interface check." " Generates position and rotation angle of each node." " The top displacements of the target building calculated as sin()" " function." " Horizontal displacements of each node interpolated to the vertical" " direction according to cantilever beam theory." ); argList::noBanner(); argList::noParallel(); // Geometric argList::addOption ( "nodes", "N", "The number of oscillating nodes (default: 10)" ); argList::addOption ( "height", "value", "Height of target building (m)" ); argList::addOption ( "period", "(time time time)", "The proper period (sec)" ); argList::addOption ( "amplitude", "(value value value)", "The amplitude" ); // Time control argList::addOption ( "time", "value", "The time to use" ); argList::addOption ( "deltaT", "value", "The time increment for multiple time loops" ); argList::addOption ( "nTimes", "value", "The number of time loops" ); // Query, output argList::addBoolOption ( "query", "Report values only and exit" ); argList::addOption ( "output", "file", "write to file, with header" ); argList::addOption ( "scale", "factor", "Scaling factor for movement (default: 1)" ); argList::addOption ( "visual-length", "len", "Visualization length for planes (visualized as triangles)" ); // Run controls argList::addDryRunOption ( "Test movement without a mesh" ); argList::addBoolOption ( "removeLock", "Remove lock-file on termination of slave" ); argList::addBoolOption ( "slave", "Invoke as a slave responder for testing" ); #include "setRootCase.H" // The oscillator position_generator gen; args.readIfPresent("nodes", gen.nDivisions); args.readIfPresent("height", gen.height); args.readIfPresent("period", gen.period); args.readIfPresent("amplitude", gen.amplitude); // Control parameters const bool dryrun = args.dryRun(); const bool slave = args.found("slave"); const bool removeLock = args.found("removeLock"); const bool optQuery = args.found("query"); const fileName outputFile(args.getOrDefault("output", "")); const scalar relax = args.getOrDefault("scale", 1); args.readIfPresent("visual-length", lumpedPointState::visLength); // Time parameters scalar currTime = args.getOrDefault("time", 0); const scalar deltaT = args.getOrDefault("deltaT", 0.001); const label nTimes = args.getOrDefault