-
Notifications
You must be signed in to change notification settings - Fork 517
Expand file tree
/
Copy pathMoorDyn_IO.f90
More file actions
1915 lines (1561 loc) · 94.1 KB
/
MoorDyn_IO.f90
File metadata and controls
1915 lines (1561 loc) · 94.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
!**********************************************************************************************************************************
! LICENSING
! Copyright (C) 2020-2021 Alliance for Sustainable Energy, LLC
! Copyright (C) 2015-2019 Matthew Hall
!
! This file is part of MoorDyn.
!
! 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.
!
!**********************************************************************************************************************************
MODULE MoorDyn_IO
! This MODULE stores variables used for input and output and provides i/o subs
USE NWTC_Library
USE MoorDyn_Types
IMPLICIT NONE
PRIVATE
INTEGER(IntKi), PARAMETER :: wordy = 0 ! verbosity level. >1 = more console output
INTEGER, PARAMETER :: nCoef = 30 ! maximum number of entries to allow in nonlinear coefficient lookup tables
! it would be nice if the above worked for everything, but I think it needs to also be matched in the Registry
! --------------------------- Output definitions -----------------------------------------
! The following are some definitions for use with the output options in MoorDyn.
! These are for the global output quantities specified by OutList, not line-specific outputs.
! Output definitions follow the structure described by the MD_OutParmType .
! Each output channel is described by the following fields:
! Name - (string) what appears at the top of the output column
! Units - (string) selected from UnitList (see below) based on index QType
! OType - (int) the type of object the output is from. 1=line, 2=point (0=invalid)
! ObjID - (int) the ID number of the line or point
! QType - (int) the type of quantity to output. 0=tension, 1=x pos, etc. see the parameters below
! NodeID - (int) the ID number of the node of the output quantity
! These are the "OTypes": 1=Line, 2=point, 3=Rod, 4=Body
! Indices for computing output channels: - customized for the MD_OutParmType approach
! these are the "QTypes"
INTEGER, PARAMETER :: Time = 0
INTEGER, PARAMETER :: PosX = 1
INTEGER, PARAMETER :: PosY = 2
INTEGER, PARAMETER :: PosZ = 3
INTEGER, PARAMETER :: RotX = 4
INTEGER, PARAMETER :: RotY = 5
INTEGER, PARAMETER :: RotZ = 6
INTEGER, PARAMETER :: VelX = 7
INTEGER, PARAMETER :: VelY = 8
INTEGER, PARAMETER :: VelZ = 9
INTEGER, PARAMETER :: RVelX = 10
INTEGER, PARAMETER :: RVelY = 11
INTEGER, PARAMETER :: RVelZ = 12
INTEGER, PARAMETER :: AccX = 13
INTEGER, PARAMETER :: AccY = 14
INTEGER, PARAMETER :: AccZ = 15
INTEGER, PARAMETER :: RAccX = 16
INTEGER, PARAMETER :: RAccY = 17
INTEGER, PARAMETER :: RAccZ = 18
INTEGER, PARAMETER :: Ten = 19
INTEGER, PARAMETER :: FX = 20
INTEGER, PARAMETER :: FY = 21
INTEGER, PARAMETER :: FZ = 22
INTEGER, PARAMETER :: MX = 23
INTEGER, PARAMETER :: MY = 24
INTEGER, PARAMETER :: MZ = 25
INTEGER, PARAMETER :: Sub = 26
INTEGER, PARAMETER :: TenA = 27
INTEGER, PARAMETER :: TenB = 28
! List of units corresponding to the quantities parameters for QTypes
CHARACTER(ChanLen), PARAMETER :: UnitList(0:26) = (/ &
"(s) ","(m) ","(m) ","(m) ", &
"(deg) ","(deg) ","(deg) ", &
"(m/s) ","(m/s) ","(m/s) ", &
"(deg/s) ","(deg/s) ","(deg/s) ", &
"(m/s2) ","(m/s2) ","(m/s2) ", &
"(deg/s2) ","(deg/s2) ","(deg/s2) ", &
"(N) ","(N) ","(N) ","(N) ", &
"(Nm) ","(Nm) ","(Nm) ","(frac) "/)
CHARACTER(28), PARAMETER :: OutPFmt = "( I4, 3X,A 10,1 X, A10 )" ! Output format parameter output list.
CHARACTER(28), PARAMETER :: OutSFmt = "ES10.3E2"
! output naming scheme is as
! examples:
! FairTen1, AnchTen1
! POINT1PX
! P3VY (Point 3, y velocity)
! L2N4PX (line 2, node 4, x position)
! ---------------------------------------------------------------------------------------------------------
! PUBLIC :: MDIO_ReadInput
PUBLIC :: setupBathymetry
PUBLIC :: getCoefficientOrCurve
PUBLIC :: SplitByBars
PUBLIC :: DecomposeString
PUBLIC :: MDIO_OpenOutput
PUBLIC :: MDIO_CloseOutput
PUBLIC :: MDIO_ProcessOutList
PUBLIC :: MDIO_WriteOutputs
PUBLIC :: Line_GetNodeTen
CONTAINS
SUBROUTINE setupBathymetry(inputString, defaultDepth, BathGrid, BathGrid_Xs, BathGrid_Ys, ErrStat3, ErrMsg3)
! SUBROUTINE getBathymetry(inputString, BathGrid, BathGrid_Xs, BathGrid_Ys, BathGrid_npoints, ErrStat3, ErrMsg3)
CHARACTER(40), INTENT(IN ) :: inputString ! string describing water depth or bathymetry filename
REAL(ReKi), INTENT(IN ) :: defaultDepth ! depth to use if inputString is empty
REAL(DbKi), ALLOCATABLE, INTENT(INOUT) :: BathGrid (:,:)
REAL(DbKi), ALLOCATABLE, INTENT(INOUT) :: BathGrid_Xs (:)
REAL(DbKi), ALLOCATABLE, INTENT(INOUT) :: BathGrid_Ys (:)
INTEGER(IntKi), INTENT( OUT) :: ErrStat3 ! Error status of the operation
CHARACTER(*), INTENT( OUT) :: ErrMsg3 ! Error message if ErrStat /= ErrID_None
INTEGER(IntKi) :: I
INTEGER(IntKi) :: UnCoef ! unit number for coefficient input file
INTEGER(IntKi) :: ErrStat4
CHARACTER(120) :: ErrMsg4
CHARACTER(4096) :: Line2
CHARACTER(20) :: nGridX_string ! string to temporarily hold the nGridX string from Line2
CHARACTER(20) :: nGridY_string ! string to temporarily hold the nGridY string from Line3
INTEGER(IntKi) :: nGridX ! integer of the size of BathGrid_Xs
INTEGER(IntKi) :: nGridY ! integer of the size of BathGrid_Ys
IF (LEN_TRIM(inputString) == 0) THEN
! If the input is empty (not provided), make the 1x1 bathymetry grid using the default depth
ALLOCATE(BathGrid(1,1), STAT=ErrStat4)
BathGrid(1,1) = REAL(defaultDepth,R8Ki)
ALLOCATE(BathGrid_Xs(1), STAT=ErrStat4)
BathGrid_Xs(1) = 0.0_DbKi
ALLOCATE(BathGrid_Ys(1), STAT=ErrStat4)
BathGrid_Ys(1) = 0.0_DbKi
ELSE IF (SCAN(inputString, "abcdfghijklmnopqrstuvwxyzABCDFGHIJKLMNOPQRSTUVWXYZ") == 0) THEN
! If the input does not have any of these string values, let's treat it as a number but store in a matrix
ALLOCATE(BathGrid(1,1), STAT=ErrStat4)
READ(inputString, *, IOSTAT=ErrStat4) BathGrid(1,1)
ALLOCATE(BathGrid_Xs(1), STAT=ErrStat4)
BathGrid_Xs(1) = 0.0_DbKi
ALLOCATE(BathGrid_Ys(1), STAT=ErrStat4)
BathGrid_Ys(1) = 0.0_DbKi
ELSE ! otherwise interpret the input as a file name to load the bathymetry lookup data from
CALL WrScr(" The depth input contains letters so will load a bathymetry file.")
! load lookup table data from file
CALL GetNewUnit( UnCoef ) ! unit number for coefficient input file
CALL OpenFInpFile( UnCoef, TRIM(inputString), ErrStat4, ErrMsg4 )
cALL SetErrStat(ErrStat4, ErrMsg4, ErrStat3, ErrMsg3, 'MDIO_getBathymetry')
READ(UnCoef,'(A)',IOSTAT=ErrStat4) Line2 ! skip the first title line
READ(UnCoef,*,IOSTAT=ErrStat4) nGridX_string, nGridX ! read in the second line as the number of x values in the BathGrid
READ(UnCoef,*,IOSTAT=ErrStat4) nGridY_string, nGridY ! read in the third line as the number of y values in the BathGrid
! Allocate the bathymetry matrix and associated grid x and y values
ALLOCATE(BathGrid(nGridY, nGridX), STAT=ErrStat4)
ALLOCATE(BathGrid_Xs(nGridX), STAT=ErrStat4)
ALLOCATE(BathGrid_Ys(nGridY), STAT=ErrStat4)
DO I = 1, nGridY+1 ! loop through each line in the rest of the bathymetry file
READ(UnCoef,'(A)',IOSTAT=ErrStat4) Line2 ! read into a line and call it Line2
IF (ErrStat4 > 0) EXIT
IF (I==1) THEN ! if it's the first line in the Bathymetry Grid, then it's a list of all the x values
READ(Line2, *,IOSTAT=ErrStat4) BathGrid_Xs
ELSE ! if it's not the first line, then the first value is a y value and the rest are the depth values
READ(Line2, *,IOSTAT=ErrStat4) BathGrid_Ys(I-1), BathGrid(I-1,:)
ENDIF
END DO
IF (I < 2) THEN
ErrStat3 = ErrID_Fatal
ErrMsg3 = "Less than the minimum of 2 data lines found in file "//TRIM(inputString)
CLOSE (UnCoef)
RETURN
ELSE
! BathGrid_npoints = nGridX*nGridY ! save the number of points in the grid
CLOSE (UnCoef)
END IF
END IF
END SUBROUTINE setupBathymetry
! read in stiffness/damping coefficient or load nonlinear data file if applicable
SUBROUTINE getCoefficientOrCurve(inputString, LineProp_c, LineProp_npoints, LineProp_Xs, LineProp_Ys, ErrStat3, ErrMsg3)
CHARACTER(40), INTENT(IN ) :: inputString
REAL(DbKi), INTENT(INOUT) :: LineProp_c
INTEGER(IntKi), INTENT( OUT) :: LineProp_nPoints
REAL(DbKi), INTENT( OUT) :: LineProp_Xs (nCoef)
REAL(DbKi), INTENT( OUT) :: LineProp_Ys (nCoef)
INTEGER(IntKi), INTENT( OUT) :: ErrStat3 ! Error status of the operation
CHARACTER(*), INTENT( OUT) :: ErrMsg3 ! Error message if ErrStat /= ErrID_None
INTEGER(IntKi) :: I
INTEGER(IntKi) :: UnCoef ! unit number for coefficient input file
INTEGER(IntKi) :: ErrStat4
CHARACTER(120) :: ErrMsg4
CHARACTER(120) :: Line2
if (SCAN(inputString, "abcdfghijklmnopqrstuvwxyzABCDFGHIJKLMNOPQRSTUVWXYZ") == 0) then ! "eE" are exluded as they're used for scientific notation!
! "found NO letter in the line coefficient value so treating it as a number."
READ(inputString, *, IOSTAT=ErrStat4) LineProp_c ! convert the entry string into a real number
LineProp_npoints = 0;
else ! otherwise interpet the input as a file name to load stress-strain lookup data from
CALL WrScr1(" Found a letter in the line EA coefficient value so will try to load the filename.")
LineProp_c = 0.0
! load lookup table data from file
CALL GetNewUnit( UnCoef )
CALL OpenFInpFile( UnCoef, TRIM(inputString), ErrStat4, ErrMsg4 ) ! add error handling?
IF (ErrStat4 == ErrID_Fatal) then
ErrStat3 = ErrStat4
ErrMsg3 = ErrMsg4
RETURN
ENDIF
READ(UnCoef,'(A)',IOSTAT=ErrStat4) Line2 ! skip the first three lines (title, names, and units) then parse
READ(UnCoef,'(A)',IOSTAT=ErrStat4) Line2
READ(UnCoef,'(A)',IOSTAT=ErrStat4) Line2
DO I = 1, nCoef
READ(UnCoef,'(A)',IOSTAT=ErrStat4) Line2 !read into a line
IF (ErrStat4 > 0) then
CALL WrScr("Error while reading lookup table file")
EXIT
ELSE IF (ErrStat4 < 0) then
CALL WrScr("Read "//trim(Int2LStr(I-1))//" data lines from lookup table file")
EXIT
ELSE
READ(Line2,*,IOSTAT=ErrStat4) LineProp_Xs(I), LineProp_Ys(I)
END IF
END DO
if (I < 2) then
ErrStat3 = ErrID_Fatal
ErrMsg3 = "Less than the minimum of 2 data lines found in file "//TRIM(inputString)//" (first 3 lines are headers)."
LineProp_npoints = 0
Close (UnCoef)
RETURN
else
LineProp_npoints = I-1
Close (UnCoef)
end if
END IF
END SUBROUTINE getCoefficientOrCurve
! Split a string into separate strings by the bar (|) symbol
SUBROUTINE SplitByBars(instring, n, outstrings)
CHARACTER(*), INTENT(INOUT) :: instring
INTEGER(IntKi), INTENT( OUT) :: n
CHARACTER(40), INTENT(INOUT) :: outstrings(6) ! array of output strings. Up to 6 strings can be read
INTEGER :: pos1, pos2
n = 0
pos1=1
DO
pos2 = INDEX(instring(pos1:), "|") ! find index of next comma
IF (pos2 == 0) THEN ! if there isn't another comma, read the last entry and call it done (this could be the only entry if no commas)
n = n + 1
outstrings(n) = instring(pos1:)
EXIT
END IF
n = n + 1
if (n > 6) then
CALL WrScr("ERROR - SplitByBars cannot do more than 6 entries")
end if
outstrings(n) = instring(pos1:pos1+pos2-2)
pos1 = pos2+pos1
END DO
END SUBROUTINE SplitByBars
! Split a string into separate letter strings and integers. Letters are converted to uppercase.
SUBROUTINE DecomposeString(outWord, let1, num1, let2, num2, let3)
CHARACTER(*), INTENT(INOUT) :: outWord
CHARACTER(25), INTENT( OUT) :: let1
! INTEGER(IntKi), INTENT( OUT) :: num1
CHARACTER(25), INTENT( OUT) :: num1
CHARACTER(25), INTENT( OUT) :: let2
CHARACTER(25), INTENT( OUT) :: num2
! INTEGER(IntKi), INTENT( OUT) :: num2
CHARACTER(25), INTENT( OUT) :: let3
! INTEGER(IntKi) :: I ! Generic loop-counting index
! CHARACTER(ChanLen) :: OutListTmp ! A string to temporarily hold OutList(I), the name of each output channel
! CHARACTER(ChanLen) :: qVal ! quantity type string to match to list of valid options
! INTEGER :: oID ! ID number of connect or line object
! INTEGER :: nID ! ID number of node object
INTEGER :: i1 = 0 ! indices of start of numbers or letters in OutListTmp string, for parsing
INTEGER :: i2 = 0
INTEGER :: i3 = 0
INTEGER :: i4 = 0
CALL Conv2UC(outWord) ! convert to all uppercase for string matching purposes
! start these strings as empty, and fill in only if used
let1 = ''
num1 = ''
let2 = ''
num2 = ''
let3 = ''
! find indicies of changes in number-vs-letter in characters of outWord and split into segments accordingly
i1 = scan( outWord , '1234567890' ) ! find index of first number in the string
if (i1 > 0) then ! if there is a number
let1 = TRIM(outWord( 1:i1-1))
i2 = i1+verify( outWord(i1+1:) , '1234567890' ) ! find starting index of second set of letters (if first character is a letter, i.e. i1>1), otherwise index of first letter
if (i2 > i1) then ! if there is a second letter/word
num1 = TRIM(outWord(i1:i2-1))
i3 = i2+scan( outWord(i2+1:) , '1234567890' ) ! find starting index of second set of numbers <<<<
if (i3 > i2) then ! if there is a second number
let2 = TRIM(outWord(i2:i3-1))
i4 = i3+verify( outWord(i3+1:) , '1234567890' ) ! third letter start
if (i4 > i3) then ! if there is a third letter/word
num2 = TRIM(outWord(i3:i4-1))
let3 = TRIM(outWord(i4: ))
else
num2 = TRIM(outWord(i3:))
end if
else
let2 = TRIM(outWord(i2:))
end if
else
num1 = TRIM(outWord(i1:))
end if
else
let1 = TRIM(outWord)
end if
!READ(outWord(i1:i2-1)) num1
!READ(outWord(i3:i4-1)) num2
! print *, "Decomposed string ", outWord, " into:"
! print *, let1
! print *, num1
! print *, let2
! print *, num2
! print *, let3
! print *, "based on indices (i1-i4):"
! print *, i1
! print *, i2
! print *, i3
! print *, i4
END SUBROUTINE DecomposeString
! ====================================================================================================
SUBROUTINE MDIO_ProcessOutList(OutList, p, m, y, InitOut, ErrStat, ErrMsg )
! This routine processes the output channels requested by OutList, checking for validity and setting
! the p%OutParam structures (of type MD_OutParmType) for each valid output.
! It assumes the value p%NumOuts has been set beforehand, and sets the values of p%OutParam.
IMPLICIT NONE
! Passed variables
CHARACTER(ChanLen), INTENT(IN) :: OutList(:) ! The list of user-requested outputs
TYPE(MD_ParameterType), INTENT(INOUT) :: p ! The module parameters
TYPE(MD_MiscVarType), INTENT(INOUT) :: m
TYPE(MD_OutputType), INTENT(INOUT) :: y ! Initial system outputs (outputs are not calculated; only the output mesh is initialized)
TYPE(MD_InitOutputType), INTENT(INOUT) :: InitOut ! Output for initialization routine
INTEGER(IntKi), INTENT(OUT) :: ErrStat ! The error status code
CHARACTER(*), INTENT(OUT) :: ErrMsg ! The error message, if an error occurred
! Local variables
INTEGER :: I ! Generic loop-counting index
! INTEGER :: J ! Generic loop-counting index
! INTEGER :: INDX ! Index for valid arrays
CHARACTER(ChanLen) :: OutListTmp ! A string to temporarily hold OutList(I), the name of each output channel
CHARACTER(ChanLen) :: qVal ! quantity type string to match to list of valid options
INTEGER :: oID ! ID number of point or line object
INTEGER :: nID ! ID number of node object
! INTEGER :: i1,i2,i3,i4 ! indices of start of numbers or letters in OutListTmp string, for parsing
CHARACTER(25) :: let1 ! strings used for splitting and parsing identifiers
CHARACTER(25) :: num1
CHARACTER(25) :: let2
CHARACTER(25) :: num2
CHARACTER(25) :: let3
INTEGER(IntKi) :: LineNumOuts ! number of entries in LineWrOutput for each line
INTEGER(IntKi) :: RodNumOuts ! same for Rods
! see the top of the module for info on the output labelling types
! Initialize values
ErrStat = ErrID_None
ErrMsg = ""
ALLOCATE ( p%OutParam(1:p%NumOuts) , STAT=ErrStat ) ! note: I'm skipping the time output entry at index 0 for simplicity
IF ( ErrStat /= 0_IntKi ) THEN
ErrStat = ErrID_Fatal
ErrMsg = "Error allocating memory for the MoorDyn OutParam array."
RETURN
ELSE
ErrStat = ErrID_None
ENDIF
! Set index, name, and units for the time output channel: ! note: I'm skipping the time output entry at index 0
!p%OutParam(0)%Indx = Time
!p%OutParam(0)%Name = "Time" ! OutParam(0) is the time channel by default.
!p%OutParam(0)%Units = "(s)"
!p%OutParam(0)%SignM = 1
! Set index, name, and units for all of the output channels.
! If a selected output channel is not valid set ErrStat = ErrID_Warn.
! go through list of requested output names and process (this is a bit of a mess)
DO I = 1,p%NumOuts
OutListTmp = OutList(I) ! current requested output name
call DecomposeString(OutListTmp, let1, num1, let2, num2, let3)
!p%OutParam(I)%Name = OutListTmp
CALL Conv2UC(OutListTmp) ! convert to all uppercase for string matching purposes
! ! find indicies of changes in number-vs-letter in characters of OutListTmp
! i1 = scan( OutListTmp , '1234567890' ) ! first number in the string
! i2 = i1+verify( OutListTmp(i1+1:) , '1234567890' ) ! second letter start (assuming first character is a letter, i.e. i1>1)
! i3 = i2+scan( OutListTmp(i2+1:) , '1234567890' ) ! second number start
! i4 = i3+verify( OutListTmp(i3+1:) , '1234567890' ) ! third letter start
! error check
! IF (i1 <= 1) THEN
! CALL DenoteInvalidOutput(p%OutParam(I)) ! flag as invalid
! CALL WrScr('Warning: invalid output specifier '//trim(OutListTmp)//'. Starting character must be C or L.')
! CYCLE ! <<<<<<<<<<< check correct usage
! END IF
p%OutParam(I)%Name = OutListTmp ! label channel with whatever name was inputted, for now
! figure out what type of output it is and process accordingly
! fairlead tension case (updated) <<<<<<<<<<<<<<<<<<<<<<<<<<< these are not currently working - need new way to find ObjID
IF (let1 == 'FAIRTEN') THEN
p%OutParam(I)%OType = 1 ! line object type
p%OutParam(I)%QType = Ten ! tension quantity type
p%OutParam(I)%Units = UnitList(Ten) ! set units according to QType
READ (num1,*) oID ! this is the line number
p%OutParam(I)%ObjID = oID ! record the ID of the line
p%OutParam(I)%NodeID = m%LineList(oID)%N ! specify node N (end B, fairlead)
! >>> should check validity of ObjID and NodeID <<<
! achor tension case
ELSE IF (let1 == 'ANCHTEN') THEN
p%OutParam(I)%OType = 1 ! line object type
p%OutParam(I)%QType = Ten ! tension quantity type
p%OutParam(I)%Units = UnitList(Ten) ! set units according to QType
READ (num1,*) oID ! this is the line number
p%OutParam(I)%ObjID = oID ! record the ID of the line
p%OutParam(I)%NodeID = 0 ! specify node 0 (end A, anchor)
! more general case
ELSE
! object number
IF (num1/=" ") THEN
READ (num1,*) oID
p%OutParam(I)%ObjID = oID ! line or point ID number
ELSE
CALL DenoteInvalidOutput(p%OutParam(I)) ! flag as invalid
CALL WrScr('Warning: invalid output specifier '//trim(OutListTmp)//'. Object ID missing.')
CYCLE
END IF
! what object type?
! Line case
IF (let1(1:1) == 'L') THEN ! Look for L?N?xxxx
p%OutParam(I)%OType = 1 ! Line object type
! for now we'll just assume the next character(s) are "n" to represent node number or "s" to represent segment number
IF (num2/=" ") THEN
READ (num2,*) nID ! node or segment ID
p%OutParam(I)%NodeID = nID
qVal = let3 ! quantity type string
ELSE IF (let2 == 'TENA' .OR. let2 == 'TA' .OR. let2(1:2) == 'NA') THEN
p%OutParam(I)%NodeID = 0
IF (let2(1:2) == 'NA') THEN
let2 = let2(3:)
END IF
qVal = let2
ELSE IF (let2 == 'TENB' .OR. let2 == 'TB' .OR. let2(1:2) == 'NB') THEN
p%OutParam(I)%NodeID = m%LineList(p%OutParam(I)%ObjID)%N
IF (let2(1:2) == 'NB') THEN
let2 = let2(3:)
END IF
qVal = let2
ELSE IF (num2 == ' ') THEN
p%OutParam(I)%NodeID = 0
qVal = let2
ELSE
CALL DenoteInvalidOutput(p%OutParam(I)) ! flag as invalid
CALL WrScr('Warning: invalid output specifier '//trim(OutListTmp)//'. Line ID or Node ID missing or incorrect tension flag.')
CYCLE
END IF
! Point case
ELSE IF (let1(1:1) == 'P' .OR. let1(1:1) == 'C') THEN ! Look for P?xxx or Point?xxx (C?xxx and Con?xxx for backwards compatability)
p%OutParam(I)%OType = 2 ! Point object type
qVal = let2 ! quantity type string
! Rod case
ELSE IF (let1(1:1) == 'R') THEN ! Look for R?xxx or Rod?xxx
p%OutParam(I)%OType = 3 ! Rod object type
IF (LEN_TRIM(let3)== 0) THEN ! No third character cluster indicates this is a whole-rod channel or endpoint
IF (let2(1:2) == 'NA') THEN
p%OutParam(I)%NodeID = 0
let2 = let2(3:)
ELSE IF (let2(1:2) == 'NB') THEN
p%OutParam(I)%NodeID = m%RodList(p%OutParam(I)%ObjID)%N
let2 = let2(3:)
ELSE
p%OutParam(I)%NodeID = -1
END IF
qVal = let2 ! quantity type string
ELSE IF (num2/=" ") THEN
READ (num2,*) nID ! rod node ID
p%OutParam(I)%NodeID = nID
qVal = let3 ! quantity type string
ELSE
CALL DenoteInvalidOutput(p%OutParam(I)) ! flag as invalid
CALL WrScr('Warning: invalid output specifier '//trim(OutListTmp)//'. Rod ID or Node ID missing.')
CYCLE
END IF
! Body case
ELSE IF (Let1(1:1) == 'B') THEN ! Look for B?xxx or Body?xxx
p%OutParam(I)%OType = 4 ! Body object type
qVal = let2 ! quantity type string
! should do fairlead option also!
! error
ELSE
CALL DenoteInvalidOutput(p%OutParam(I)) ! flag as invalid
CALL WrScr('Warning: invalid output specifier '//trim(OutListTmp)//'. Must start with L, R, or B')
CYCLE
END IF
! which kind of quantity?
IF (qVal == 'PX') THEN
p%OutParam(I)%QType = PosX
p%OutParam(I)%Units = UnitList(PosX)
ELSE IF (qVal == 'PY') THEN
p%OutParam(I)%QType = PosY
p%OutParam(I)%Units = UnitList(PosY)
ELSE IF (qVal == 'PZ') THEN
p%OutParam(I)%QType = PosZ
p%OutParam(I)%Units = UnitList(PosZ)
ELSE IF (qVal == 'RX') THEN
p%OutParam(I)%QType = RotX
p%OutParam(I)%Units = UnitList(RotX)
ELSE IF (qVal == 'RY') THEN
p%OutParam(I)%QType = RotY
p%OutParam(I)%Units = UnitList(RotY)
ELSE IF (qVal == 'RZ') THEN
p%OutParam(I)%QType = RotZ
p%OutParam(I)%Units = UnitList(RotZ)
ELSE IF (qVal == 'VX') THEN
p%OutParam(I)%QType = VelX
p%OutParam(I)%Units = UnitList(VelX)
ELSE IF (qVal == 'VY') THEN
p%OutParam(I)%QType = VelY
p%OutParam(I)%Units = UnitList(VelY)
ELSE IF (qVal == 'VZ') THEN
p%OutParam(I)%QType = VelZ
p%OutParam(I)%Units = UnitList(VelZ)
ELSE IF (qVal == 'RVX') THEN
p%OutParam(I)%QType = RVelX
p%OutParam(I)%Units = UnitList(RVelX)
ELSE IF (qVal == 'RVY') THEN
p%OutParam(I)%QType = RVelY
p%OutParam(I)%Units = UnitList(RVelY)
ELSE IF (qVal == 'RVZ') THEN
p%OutParam(I)%QType = RVelZ
p%OutParam(I)%Units = UnitList(RVelZ)
ELSE IF (qVal == 'AX') THEN
p%OutParam(I)%QType = AccX
p%OutParam(I)%Units = UnitList(AccX)
ELSE IF (qVal == 'AY') THEN ! fixed typo Nov 24
p%OutParam(I)%QType = AccY
p%OutParam(I)%Units = UnitList(AccY)
ELSE IF (qVal == 'AZ') THEN
p%OutParam(I)%QType = AccZ
p%OutParam(I)%Units = UnitList(AccZ)
ELSE IF (qVal == 'RAX') THEN
p%OutParam(I)%QType = RAccX
p%OutParam(I)%Units = UnitList(RAccX)
ELSE IF (qVal == 'RAY') THEN ! fixed typo Nov 24
p%OutParam(I)%QType = RAccY
p%OutParam(I)%Units = UnitList(RAccY)
ELSE IF (qVal == 'RAZ') THEN
p%OutParam(I)%QType = RAccZ
p%OutParam(I)%Units = UnitList(RAccZ)
ELSE IF ((qVal == 'T') .OR. (qVal == 'TEN')) THEN
p%OutParam(I)%QType = Ten
p%OutParam(I)%Units = UnitList(Ten)
ELSE IF ((qVal == 'TA') .OR. (qVal == 'TENA')) THEN
p%OutParam(I)%QType = TenA
p%OutParam(I)%Units = UnitList(Ten)
ELSE IF ((qVal == 'TB') .OR. (qVal == 'TENB')) THEN
p%OutParam(I)%QType = TenB
p%OutParam(I)%Units = UnitList(Ten)
ELSE IF (qVal == 'FX') THEN
p%OutParam(I)%QType = FX
p%OutParam(I)%Units = UnitList(FX)
ELSE IF (qVal == 'FY') THEN
p%OutParam(I)%QType = FY
p%OutParam(I)%Units = UnitList(FY)
ELSE IF (qVal == 'FZ') THEN
p%OutParam(I)%QType = FZ
p%OutParam(I)%Units = UnitList(FZ) ! <<<< should add moments as well <<<<
ELSE IF (qVal == 'MX') THEN
p%OutParam(I)%QType = MX
p%OutParam(I)%Units = UnitList(MX)
ELSE IF (qVal == 'MY') THEN
p%OutParam(I)%QType = MY
p%OutParam(I)%Units = UnitList(MY)
ELSE IF (qVal == 'MZ') THEN
p%OutParam(I)%QType = MZ
p%OutParam(I)%Units = UnitList(MZ) ! <<<< should add moments as well <<<<
ELSE IF (qVal == 'SUB') THEN
p%OutParam(I)%QType = Sub
p%OutParam(I)%Units = UnitList(Sub)
ELSE
CALL DenoteInvalidOutput(p%OutParam(I)) ! flag as invalid
CALL WrScr('Warning: invalid output specifier '//trim(OutListTmp)//'. Quantity type not recognized.')
CONTINUE
END IF
END IF
! also check whether each object index and node index (if applicable) is in range
IF (p%OutParam(I)%OType==1) THEN ! Line
IF (p%OutParam(I)%ObjID > p%NLines) THEN
CALL WrScr('Warning: output Line index excedes number of Lines in requested output '//trim(OutListTmp)//'.')
CALL DenoteInvalidOutput(p%OutParam(I)) ! flag as invalid
END IF
IF (p%OutParam(I)%NodeID > m%LineList(p%OutParam(I)%ObjID)%N) THEN
CALL WrScr('Warning: output node index excedes number of nodes in requested output '//trim(OutListTmp)//'.')
CALL DenoteInvalidOutput(p%OutParam(I)) ! flag as invalid
ELSE IF (p%OutParam(I)%NodeID < 0) THEN
CALL WrScr('Warning: output node index is less than zero in requested output '//trim(OutListTmp)//'.')
CALL DenoteInvalidOutput(p%OutParam(I)) ! flag as invalid
END IF
ELSE IF (p%OutParam(I)%OType==2) THEN ! point
IF (p%OutParam(I)%ObjID > p%NPoints) THEN
CALL WrScr('Warning: output point index excedes number of points in requested output '//trim(OutListTmp)//'.')
CALL DenoteInvalidOutput(p%OutParam(I)) ! flag as invalid
END IF
ELSE IF (p%OutParam(I)%OType==3) THEN ! Rod
IF (p%OutParam(I)%ObjID > p%NRods) THEN
CALL WrScr('Warning: output Rod index excedes number of Rods in requested output '//trim(OutListTmp)//'.')
CALL DenoteInvalidOutput(p%OutParam(I)) ! flag as invalid
END IF
IF (p%OutParam(I)%NodeID > m%RodList(p%OutParam(I)%ObjID)%N) THEN
CALL WrScr('Warning: output node index excedes number of nodes in requested output '//trim(OutListTmp)//'.')
CALL DenoteInvalidOutput(p%OutParam(I)) ! flag as invalid
ELSE IF (p%OutParam(I)%NodeID < -1) THEN
CALL WrScr('Warning: output node index is less than -1 in requested output '//trim(OutListTmp)//'.')
CALL DenoteInvalidOutput(p%OutParam(I)) ! flag as invalid
END IF
ELSE IF (p%OutParam(I)%OType==4) THEN ! Body
IF (p%OutParam(I)%ObjID > p%NBodies) THEN
CALL WrScr('Warning: output Body index excedes number of Bodies in requested output '//trim(OutListTmp)//'.')
CALL DenoteInvalidOutput(p%OutParam(I)) ! flag as invalid
END IF
END IF
! is the reverse sign functionality necessary?
! ! Reverse the sign (+/-) of the output channel if the user prefixed the
! ! channel name with a "-", "_", "m", or "M" character indicating "minus".
END DO ! I ... looping through OutList
!! ! Allocate MDWrOutput which is used to store a time step's worth of output channels, prior to writing to a file.
! ALLOCATE( MDWrOutput( p%NumOuts), STAT = ErrStat )
! IF ( ErrStat /= ErrID_None ) THEN
! ErrMsg = ' Error allocating space for MDWrOutput array.'
! ErrStat = ErrID_Fatal
! RETURN
! END IF
! Allocate MDWrOuput2 which is used to store a time step's worth of output data for each line, just making it really big for now <<<<<<<<<<<<<<
! <<<<<<<<<<< should do this for each line instead.
! ALLOCATE( LineWriteOutputs( 200), STAT = ErrStat )
! IF ( ErrStat /= ErrID_None ) THEN
! ErrMsg = ' Error allocating space for LineWriteOutputs array.'
! ErrStat = ErrID_Fatal
! RETURN
! END IF
!Allocate WriteOuput
ALLOCATE( y%WriteOutput( p%NumOuts), &
InitOut%WriteOutputHdr(p%NumOuts), &
InitOut%WriteOutputUnt(p%NumOuts), STAT = ErrStat )
IF ( ErrStat /= ErrID_None ) THEN
ErrMsg = ' Error allocating space for y%WriteOutput array.'
ErrStat = ErrID_Fatal
RETURN
END IF
! allocate output array in each Line
DO I=1,p%NLines
! calculate number of output entries (excluding time) to write for this line
LineNumOuts = 3*(m%LineList(I)%N + 1)*SUM(m%LineList(I)%OutFlagList(2:6)) &
+ (m%LineList(I)%N + 1)*SUM(m%LineList(I)%OutFlagList(7:9)) &
+ m%LineList(I)%N*SUM(m%LineList(I)%OutFlagList(10:18))
ALLOCATE(m%LineList(I)%LineWrOutput( 1 + LineNumOuts), STAT = ErrStat)
IF ( ErrStat /= ErrID_None ) THEN
ErrMsg = ' Error allocating space for a LineWrOutput array'
ErrStat = ErrID_Fatal
RETURN
END IF
END DO ! I
! allocate output array in each Rod
DO I=1,p%NRods
! calculate number of output entries (excluding time) to write for this Rod
RodNumOuts = 3*(m%RodList(I)%N + 1)*SUM(m%RodList(I)%OutFlagList(2:9)) &
+ (m%RodList(I)%N + 1)*SUM(m%RodList(I)%OutFlagList(10:11)) &
+ m%RodList(I)%N*SUM(m%RodList(I)%OutFlagList(12:18))
ALLOCATE(m%RodList(I)%RodWrOutput( 1 + RodNumOuts), STAT = ErrStat)
IF ( ErrStat /= ErrID_None ) THEN
ErrMsg = ' Error allocating space for a RodWrOutput array'
ErrStat = ErrID_Fatal
RETURN
END IF
END DO ! I
!print *, "y%WriteOutput allocated to size ", size(y%WriteOutput)
! These variables are to help follow the framework template, but the data in them is simply a copy of data
! already available in the OutParam data structure
! ALLOCATE ( InitOut%WriteOutputHdr(p%NumOuts+p%OutAllint*p%OutAllDims), STAT = ErrStat )
! ALLOCATE ( InitOut%WriteOutputUnt(p%NumOuts+p%OutAllint*p%OutAllDims), STAT = ErrStat )
DO I = 1,p%NumOuts
InitOut%WriteOutputHdr(I) = p%OutParam(I)%Name
InitOut%WriteOutputUnt(I) = p%OutParam(I)%Units
END DO
CONTAINS
SUBROUTINE DenoteInvalidOutput( OutParm )
TYPE(MD_OutParmType), INTENT (INOUT) :: OutParm
OutParm%OType = 0 ! flag as invalid
OutParm%Name = 'Invalid'
OutParm%Units = ' - '
END SUBROUTINE DenoteInvalidOutput
END SUBROUTINE MDIO_ProcessOutList
!----------------------------------------------------------------------------------------============
!----------------------------------------------------------------------------------------============
SUBROUTINE MDIO_OpenOutput( MD_ProgDesc, p, m, InitOut, ErrStat, ErrMsg )
!----------------------------------------------------------------------------------------------------
TYPE(ProgDesc), INTENT( IN ) :: MD_ProgDesc
TYPE(MD_ParameterType), INTENT( INOUT ) :: p
TYPE(MD_MiscVarType), INTENT( INOUT ) :: m
TYPE(MD_InitOutPutType ), INTENT( IN ) :: InitOut !
INTEGER, INTENT( OUT ) :: ErrStat ! a non-zero value indicates an error occurred
CHARACTER(*), INTENT( OUT ) :: ErrMsg ! Error message if ErrStat /= ErrID_None
INTEGER :: I ! Generic loop counter
INTEGER :: J ! Generic loop counter
CHARACTER(1024) :: OutFileName ! The name of the output file including the full path.
! INTEGER :: L ! counter for index in LineWrOutput
INTEGER :: LineNumOuts ! number of entries in LineWrOutput for each line
INTEGER :: RodNumOuts ! for Rods ... redundant <<<
CHARACTER(200) :: Frmt ! a string to hold a format statement
INTEGER :: ErrStat2
ErrStat = ErrID_None
ErrMsg = ""
p%Delim = ' ' ! for now
!-------------------------------------------------------------------------------------------------
! Open the output file, if necessary, and write the header
!-------------------------------------------------------------------------------------------------
IF ( ALLOCATED( p%OutParam ) .AND. p%NumOuts > 0 .AND. p%OutSwitch > 0) THEN ! Output has been requested so let's open an output file
! Open the file for output
OutFileName = TRIM(p%RootName)//'.out'
CALL GetNewUnit( p%MDUnOut )
CALL OpenFOutFile ( p%MDUnOut, OutFileName, ErrStat, ErrMsg )
IF ( ErrStat > ErrID_None ) THEN
ErrMsg = ' Error opening MoorDyn-level output file: '//TRIM(ErrMsg)
ErrStat = ErrID_Fatal
RETURN
END IF
! Write empty header lines (to match standard OF out file formats used by all other modules)
write (p%MDUnOut,'(/,A/)', IOSTAT=ErrStat) 'These predictions were generated by '//&
TRIM(MD_ProgDesc%Name)//' ('//TRIM(MD_ProgDesc%Ver)//TRIM(MD_ProgDesc%Date)//&
') on '//CurDate()//' at '//CurTime()//'.'
write(p%MDUnOut,*) ''
write(p%MDUnOut,*) ''
write(p%MDUnOut,*) ''
!Write the names of the output parameters:
Frmt = '(A10,'//TRIM(Int2LStr(p%NumOuts))//'(A1,A15))'
WRITE(p%MDUnOut,Frmt, IOSTAT=ErrStat2) TRIM( 'Time' ), ( p%Delim, TRIM( p%OutParam(I)%Name), I=1,p%NumOuts )
WRITE(p%MDUnOut,Frmt) TRIM( '(s)' ), ( p%Delim, TRIM( p%OutParam(I)%Units ), I=1,p%NumOuts )
! ELSE ! if no outputs requested
! call wrscr('note, MDIO_OpenOutput thinks that no outputs have been requested.')
END IF
!--------------------------------------------------------------------------
! now do the same for line output files
!--------------------------------------------------------------------------
!! allocate UnLineOuts
!ALLOCATE(UnLineOuts(p%NLines)) ! should add error checking
DO I = 1,p%NLines
IF (m%LineList(I)%OutFlagList(1) == 1) THEN ! only proceed if the line is flagged to output a file
! Open the file for output
OutFileName = TRIM(p%RootName)//'.Line'//TRIM(Int2LStr(I))//'.out'
CALL GetNewUnit( m%LineList(I)%LineUnOut )
CALL OpenFOutFile ( m%LineList(I)%LineUnOut, OutFileName, ErrStat, ErrMsg )
IF ( ErrStat > ErrID_None ) THEN
ErrMsg = ' Error opening Line output file '//TRIM(ErrMsg)
ErrStat = ErrID_Fatal
RETURN
END IF
! calculate number of output entries (excluding time) to write for this line
LineNumOuts = 3*(m%LineList(I)%N + 1)*SUM(m%LineList(I)%OutFlagList(2:6)) &
+ (m%LineList(I)%N + 1)*SUM(m%LineList(I)%OutFlagList(7:9)) &
+ m%LineList(I)%N*SUM(m%LineList(I)%OutFlagList(10:18))
if (wordy > 2) PRINT *, LineNumOuts, " output channels"
Frmt = '(A10,'//TRIM(Int2LStr(1 + LineNumOuts))//'(A1,A15))' ! should evenutally use user specified format?
!Frmt = '(A10,'//TRIM(Int2LStr(3+3*m%LineList(I)%N))//'(A1,A15))'
! Write the names of the output parameters: (these use "implied DO" loops)
WRITE(m%LineList(I)%LineUnOut,'(A10)', advance='no', IOSTAT=ErrStat2) TRIM( 'Time' )
IF (m%LineList(I)%OutFlagList(2) == 1) THEN
WRITE(m%LineList(I)%LineUnOut,'('//TRIM(Int2LStr((3+3*m%LineList(I)%N)))//'(A1,A15))', advance='no', IOSTAT=ErrStat2) &
( p%Delim, 'Node'//TRIM(Int2Lstr(J))//'px', p%Delim, 'Node'//TRIM(Int2Lstr(J))//'py', p%Delim, 'Node'//TRIM(Int2Lstr(J))//'pz', J=0,(m%LineList(I)%N) )
END IF
IF (m%LineList(I)%OutFlagList(3) == 1) THEN
WRITE(m%LineList(I)%LineUnOut,'('//TRIM(Int2LStr((3+3*m%LineList(I)%N)))//'(A1,A15))', advance='no', IOSTAT=ErrStat2) &
( p%Delim, 'Node'//TRIM(Int2Lstr(J))//'vx', p%Delim, 'Node'//TRIM(Int2Lstr(J))//'vy', p%Delim, 'Node'//TRIM(Int2Lstr(J))//'vz', J=0,(m%LineList(I)%N) )
END IF
IF (m%LineList(I)%OutFlagList(4) == 1) THEN
WRITE(m%LineList(I)%LineUnOut,'('//TRIM(Int2LStr((3+3*m%LineList(I)%N)))//'(A1,A15))', advance='no', IOSTAT=ErrStat2) &
( p%Delim, 'Node'//TRIM(Int2Lstr(J))//'Ux', p%Delim, 'Node'//TRIM(Int2Lstr(J))//'Uy', p%Delim, 'Node'//TRIM(Int2Lstr(J))//'Uz', J=0,(m%LineList(I)%N) )
END IF
IF (m%LineList(I)%OutFlagList(5) == 1) THEN
WRITE(m%LineList(I)%LineUnOut,'('//TRIM(Int2LStr((3+3*m%LineList(I)%N)))//'(A1,A15))', advance='no', IOSTAT=ErrStat2) &
( p%Delim, 'Node'//TRIM(Int2Lstr(J))//'Dx', p%Delim, 'Node'//TRIM(Int2Lstr(J))//'Dy', p%Delim, 'Node'//TRIM(Int2Lstr(J))//'Dz', J=0,(m%LineList(I)%N) )
END IF
IF (m%LineList(I)%OutFlagList(6) == 1) THEN
WRITE(m%LineList(I)%LineUnOut,'('//TRIM(Int2LStr((3+3*m%LineList(I)%N)))//'(A1,A15))', advance='no', IOSTAT=ErrStat2) &
( p%Delim, 'Node'//TRIM(Int2Lstr(J))//'bx', p%Delim, 'Node'//TRIM(Int2Lstr(J))//'by', p%Delim, 'Node'//TRIM(Int2Lstr(J))//'bz', J=0,(m%LineList(I)%N) )
END IF
IF (m%LineList(I)%OutFlagList(7) == 1) THEN
WRITE(m%LineList(I)%LineUnOut,'('//TRIM(Int2LStr((m%LineList(I)%N)))//'(A1,A15))', advance='no', IOSTAT=ErrStat2) &
( p%Delim, 'Node'//TRIM(Int2Lstr(J))//'Wz', J=0,(m%LineList(I)%N) )
END IF
IF (m%LineList(I)%OutFlagList(8) == 1) THEN
WRITE(m%LineList(I)%LineUnOut,'('//TRIM(Int2LStr((m%LineList(I)%N)))//'(A1,A15))', advance='no', IOSTAT=ErrStat2) &
( p%Delim, 'Node'//TRIM(Int2Lstr(J))//'Kurv', J=0,(m%LineList(I)%N) )
END IF
IF (m%LineList(I)%OutFlagList(10) == 1) THEN
WRITE(m%LineList(I)%LineUnOut,'('//TRIM(Int2LStr((m%LineList(I)%N)))//'(A1,A15))', advance='no', IOSTAT=ErrStat2) &
( p%Delim, 'Seg'//TRIM(Int2Lstr(J))//'Ten', J=1,(m%LineList(I)%N) )
END IF
IF (m%LineList(I)%OutFlagList(11) == 1) THEN
WRITE(m%LineList(I)%LineUnOut,'('//TRIM(Int2LStr((m%LineList(I)%N)))//'(A1,A15))', advance='no', IOSTAT=ErrStat2) &
( p%Delim, 'Seg'//TRIM(Int2Lstr(J))//'Dmp', J=1,(m%LineList(I)%N) )
END IF
IF (m%LineList(I)%OutFlagList(12) == 1) THEN
WRITE(m%LineList(I)%LineUnOut,'('//TRIM(Int2LStr((m%LineList(I)%N)))//'(A1,A15))', advance='no', IOSTAT=ErrStat2) &
( p%Delim, 'Seg'//TRIM(Int2Lstr(J))//'Str', J=1,(m%LineList(I)%N) )
END IF
IF (m%LineList(I)%OutFlagList(13) == 1) THEN
WRITE(m%LineList(I)%LineUnOut,'('//TRIM(Int2LStr((m%LineList(I)%N)))//'(A1,A15))', advance='no', IOSTAT=ErrStat2) &
( p%Delim, 'Seg'//TRIM(Int2Lstr(J))//'SRt', J=1,(m%LineList(I)%N) )
END IF
IF (m%LineList(I)%OutFlagList(14)== 1) THEN
WRITE(m%LineList(I)%LineUnOut,'('//TRIM(Int2LStr((m%LineList(I)%N)))//'(A1,A10))', advance='no', IOSTAT=ErrStat2) &
( p%Delim, 'Seg'//TRIM(Int2Lstr(J))//'Lst', J=1,(m%LineList(I)%N) )
END IF
WRITE(m%LineList(I)%LineUnOut,'(A1)', IOSTAT=ErrStat2) ' ' ! make line break at the end