-
Notifications
You must be signed in to change notification settings - Fork 31
Multibody and flexibility #86
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
The Node class will be used to describe the motions of the structure in the structural/multibody formulation. A way to see the new structural model: We could say that RAFT currently solves for the motions of a single node with 6dofs, as all the inertia, stiffness, and whatnot is lumped at the platform reference point. Our plan is that RAFT will solve for the 6dofs of an arbitrary number of nodes, which could be part of a rigid or flexible member. The Node class is not used by anything in RAFT yet.
No longer using an int to identify which part of the floater the member is part of. See commit a2816bb
Code that lived in the prototype code that was transferred to the Member class. - Added list of structural Nodes to the structure - Included structural information to Beam elements (Young's and shear moduli) - Updated setPosition() to use the position of member's structural nodes to update member's position - Function to compute the stiffness matrix of flexible members - Function to plot the structural frame
For example, using `nodeList` instead of `nodes`, using node.r to denote position, node.Xi0 for the mean position, node.Xi for the dynamic position, among other things
Major changes to the FOWT class to include nodes, joints, flexible members, a variable number of dofs (which was assumed 6), etc. That includes new variables to deal with the full set of dofs and the reduced set of dofs, and methods to create joints, attach members to joints, and to evaluate the reduced degrees of freedom. An important method is fowt.computeTransformationMatrix(), which evaluates the matrix that transforms from the set of reduced degrees of freedom to the set of full degrees of freedom.
The Rotor class now has a Node at the RNA Reference Point, which is the point at the top of the tower around which the nacelle yaws. This Node object is responsible for - providing the position and orientation of the RNA Reference Point in the global coordinate system. This is the same job as fowt.r6 used to do, but now allowing for relative motions between the tower and the FOWT. This relative motion is usually due to flexibility, in particular torsion and bending. - Store the inertia matrix of the RNA in the global reference frame. - Store the loads on the RNA, transmitting those loads to the rest of the structure via the joint between the tower and the RNA. Once flexibility/multibody capabilities are complete, the motion of this Mode will be determined by solving the equations of motion of the FOWT. The position of the Rotor's Node has to be set using fowt.setNodesPosition(), which is called within fowt.setPosition(). This Node does not follow turbine yaw, as it is set by an independent procedure. Yaw control is NOT modified by this commit.
Also needed to renumber node ids after reorganizing nodes in the temporary snippet for compatibility with previous code. Also updated some comments.
- As members will have relative motions between them, we will need the stiffness and inertia matrices wrp to their nodes. To do that, changed member.getInertia() and member.getHydrostatics() to provide results wrp to an arbitrary point instead of the platform reference point. The code is the same, but changed comments and the reference point now defaults to the member's first node. Still need to implement that for flexible members - In the same spirit, added a member.getWeight() function. This one is not used in the code yet, only in the prototype code that I have locally. - Added roll-yaw and pitch-yaw terms to the hydrostatic stiffness matrix - The member stiffness matrices now have dimension (nDOF, nDOF) instead of (6,6) for future flexible members. The rigid tests that we have so far will result in the same thing as nDOF=6. - Decided that storing stiffness and inertia matrices at node level is too convoluted, so removed the attributes that would be used to do that - Removed option to provide position to member.setPosition(). Now Member position is fully dependent on the position of it's first node. Still need to adapt this part of the code to deal with flexible members
Neglecting the roll-yaw and pitch-yaw stiffness terms for now
self.r_hub_rel_RRP (rotor reference point) was mislabeled self.r_hub_rel_PRP (platform reference point)
Also modified the function to helper function getWeightOfPointMass() to use dR directly instead of providing the application point and the reference point
The stiffness matrices and mean load vectors due to hydrostatics and weight are now computed in the reduced degrees of freedom. This is done by computing them in the full degrees of freedom (i.e., individually for each member/component) and assembling the reduced matrices/vectors with the transformation matrix. For the stiffness matrix, we also use the derivative of the transformation matrix to account for the 'geometric stiffness' term (not sure if this is the proper term).
- We were providing ID=0 to the rotor node, now it's receiving a proper id - When creating rigid links in fowt.attachMemberToJoint(), we were overwriting the joint id of nodes that already had a joint - I had removed node.K_flexible in a previous commit but forgot to remove it from member.computeStiffnessMatrix()
- The mass is now the sum of all contributions along (surge,surge) - Workaround for the 6x6 M_sub and M_all matrix used in the fowt class. Still don't know the proper way to compute them but have some ideas - Small bug fix: we were calling member.name instead of self.name when raising errors for things that weren't implemented yet
Modified calcHydroConstants() and calcHydroExcitation(), both in member and fowt classes, to comply with the direct-elimination technique, i.e., we first compute quantities in the full set of dofs and then convert them to the reduced set of dofs
Bug introduced by the modifications to consider arbitrary number of dofs
rotor.calcAero() now computes loads and matrices wrt the rotor's reference node. Updated rotor test to account for that. fowt.calcTurbineConstants() computes quantities in the reduced set of dofs
Modified model.solveStatics() and model.analyzeUnloaded() to solve the static problem considering the reduced dofs of the system instead of assuming 6dofs. Also modified fowt.plot() to account for the modifications in member.setPosition() in previous commits. Also removed some lines from fowt.calcCurrentLoads() that aren't used anymore
- Modified fowt.solveEigen() to allow for flexible components. Only dry members for now as the hydrostatic stiffness matrix is still incorrect for flexible members in the water - Modified how the hydrostatic geometric stiffness of flexible members is taken into account. Now it is in fowt.calcStatics() to allow for external weights that are acting on the flexible member (e.g. RNA atop of the tower) - Add a function to visualize mode shapes in viz3Danim (https://github.com/ebranlard/viz3Danim)
If thickness provided by the user would lead to a negative internal diameter, set the internal diameter to zero (i.e., solid cross-section)
Storing the F_moor0, C_moor, A_BEM, B_BEM, and X_BEM in matrices with nDOF instead of six. They are still lumped at the first 6 dofs as before, but doing this internally in the FOWT class instead of doing it in the model class (when possible)
We were missing the contribution of the internal force acting on nodes whose dofs were reduced. For example, the weight of a flexible tower acting on the base node, when the dofs of the base node were reduced using the dofs of another node.
Modified calcHydroLinearization and calcDragExcitation (in both Member and FOWT classes) to fit in the new multibody/structural paradigm. In the Member class, this involves accounting for flexible members; in the FOWT class, this involves computing the forces in full dofs and then use fowt.T to convert the forces to the reduced set of dofs
model.solveDynamics() now solves the equations of motion in the reduced dofs of each FOWT. There are still some important modifications to do, in particular moorings and second order loads, but these can be dealt with later. Also, the function still doesn't work for arrays of turbines with fowt.nDOF != 6. Updated true values of test_model.py::test_analyzeCases() due to new method to compute tower top acceleration
- Forgot some debug variables in member.computeInertiaMatrix_FE(). Also, now saving the inertia matrix of the member at member levels instead of just returning it - We're not storing node's dynamic displacement at node level, but keeping node.Xi commented out in case we change our mind in the future
We were modifying the state of `self` in the function and not returning it to the original state. Changed the procedure to make sure that `self` remains the same
This is one of the main steps to make solveDynamics() work with flexible structures. Seems to be working!
For flexible towers, the tower base loads are now computed based on the finite-element stiffness matrix of the member. For rigid towers, the procedure is the same as before. In the future, we could compute the loads on the joints and keep a single workflow for computing the tower based loads of both types of tower.
- When getting the heading indices in readQTF(), we were comparing the angles stored in self.heads_2nd, which are in radians, with the ones from the WAMIT-format input file, which are in degrees). Now we convert the angles from the WAMIT-format input to be in radians too. - The direction interpolation in calcHydroForce_2ndOrd() was being performed on the whole matrix instead of on the real and imaginary part separately.
- The formula to compute the wind speed amplitude from the rotor-averaged wind speed spectrum was missing a factor of 2 and dw - The floating feedback term (rotor.k_float) was missing a division by the hub height (to translate tower top translational velocity into pitch) - Update fowt.F_aero to fowt.f_aero in the Model class. Code still commented out because there's still something wrong when we activate turbulent wind forcing. - Update true values of affected tests
model.adjustBallast() uses member headings to exploit floater symmetry but this information was no longer stored at member level (see #82). This commit provides a workaround to that problem. In the future, we should overhaul model.adjustBallast() to make it more general, and probably move it to the FOWT class now that we can have multiple FOWTs in a RAFT model
Modified test files: test_fowt.py and test_model.py. Added tests of the OC4-DeepCwind FOWT using pre-computed hydrodynamic coefficients provided by WAMIT files - .1 (radiation), .3 (first-order diffraction loads), and .12 (full QTFs).
Tests using OC4semi-WAMIT_Coefs.yaml had an unnecessarily fine frequency discretization. Reducing the number of frequencies to make it run faster.
Added a circular beam and a rectangular beam to test_member.py. They are tested using previously existing test functions and a new test_StiffnessMatrix_FE() function, which is used to verify the structural stiffness matrix of beam members
- In fowt.setPosition(), changed the computation of fowt.r6 to use the same method as in fowt.setNodesPosition() - QTFs computed using the slender body do not work for FOWTs with nDOF > 6 yet, so skipping that case in fowt.calcQTF_slenderBody() - The software was throwing an error when computing the tower base moment for a FOWT with rigid and flexible/multibody floater because of the dimensions of the aerodynamic force vector - Had forgotten to implement model.solveEigen() for platforms with nDOF > 6. Did the same modifications as we had done in fowt.solveEigen().
- Included a test case 'VolturnUS-S-flexible.yaml' which is the VolturnUS semi-submersible with flexible tower and flexible pontoons. It is used in test_fowt.py and test_model.py - Most of the test functions in test_fowt.py had their True values specified directly in the .py file. This was fine because the FOWT always had 6 dofs, so arrays had a reasonable size. Now that we support FOWTs with an arbitrary number of dofs, it's unfeasible to keep all true values in text format. For this reason, this commit modifies all functions in test_fowt.py to use true values loaded from a pickle file. Didn't do that in test_model.py because it is reasonable to focus in a subset of results (for example, 10 first eigenmodes instead of all) - also removing tests/15_RAFT_rect. This folder is created by one of the tests and was uploaded to the repo in a previous commit by mistake
Had a lot of temporary code, especially for plotting. Unnecessary code was removed. Useful plotting code was moved from temporary plotting functions to the main plotting functions.
test_model.py::test_solveStaticsWave() seems to be failing due to numerical differences between my machine and github actions. Because this test case does not have mean wave drift, some values are very small in some dofs. Adopting a larger tolerance, which is still quite restrict, for this test only
- Added a comment showing how to run /examples/VolturnUS-S_example.yaml with a flexible tower - Added /examples/VolturnUS-S-flexible_example.yaml to exemplify cases with flexible floaters, which require the new input deck `joints` - Noticed that the tower of tests\test_data\VolturnUS-S-flexible.yaml had unrealistically large Young and shear moduli. I was checking if using very stiff materials would result in the same as a perfectly rigid tower, but forgot to change those values back to something more reasonable after that
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Purpose
This PR implements multibody and flexibility capabilities. In short, RAFT can now model FOWTs made of rigid and/or flexible members, and those members can be connected by joints that allow for relative motions between them (only cantilever and ball joints are supported for now).
This is a major change to the code: RAFT used to solve for the 6-dof motions of a rigid FOWT, whereas now it solves for an arbitrary number of degrees of freedom. We use a direct-elimination technique to model the joints, and Timoshenko beam elements to model flexible members. As we don't have documentation for that yet, this is a very long PR to assist users in using this new capability.
Summary of the changes introduced by this PR
What can be done
Same example from rectangular members in SubDyn https://github.com/OpenFAST/openfast/pull/2646
Usage
Two main changes in the input file are required to use it, as illustrated by the example file located in
examples/VolturnUS-S-flexible_example.yaml:The flag
typewithin each RAFTmember, including the tower, needs to be eitherrigidorbeam.rigid, the member is discretized with a single 6-dof node with loads computed in the same way as previous RAFT versionsbeam, the member is modeled using a Timoshenko beam formulation. The structural nodes are coincident with the hydrodynamic nodes used in the strip-theory approach to compute hydrodynamic loads, i.e., they are determined by the number of stations and member discretization. Loads are computed in a distributed way.If users want to model a rigid platform, all they need to do is set all platform members to
rigid---using integers, as in previous examples, will lead to an error. For a flexible tower, users need to set the tower tobeamand provide the Young's and shear moduli of the material. RAFT will then rigidly connect all members to a joint located at the reference point of the platform, (0,0,0).Users need to change member type to either `rigid` or `beam`. This is the only change needed if the platform is rigid. However, flexible platforms require input section `joints` explained below.
If, however, the platform contains any
beammember, users need an additional input section namedjoints. This section specifies how all the members of a platform are connected to each other. Those joints specify the location where the members are connected, and do not need to be at the same position as the ends of members.For example, take a look at the joint connecting the member named
pontoonand the member namedouter_column_bottomin the figure below. The joint can be mapped directly to the end node of the flexible memberpontoon, but it has a geometric offset with respect to the node corresponding to the rigid memberouter_column_bottom. RAFT will then create a rigid link (2 virtual nodes rigidly connected) connecting the joint to the node with an geometric offset.If the platform has flexible members, RAFT needs to know how those members are connected. The names listed in `members` are the user-defined names of each member. Rigid links are created automatically to account for geometric offsets.
Type of change
This is a breaking change because previous RAFT models with member type different from
rigidorbeamwill throw an error.Checklist