Unverified Commit 9be652ee authored by William Gurecky's avatar William Gurecky Committed by GitHub
Browse files

Fmu wrappers (#4647) (#288)

* wip: adds boilerplate fmi and fmu c interface

* adds working example FMU model import

* adds wrappers for doStep getReal and setReal

* fix issue with getting c++ obj pointer

* wip: debugging illegal fmu state after init

* wip: debugging illegal fmu state after init

* adds example FMU import

* adds calls to enter and exitInitilizationMode to setupExperiment and adds output to fmu step example

* adds verbosity flag for c/c++ interface fns

* remove extraneous module imports in fmu2_f_api

* example fmu step test now runs to endTime of 0.1s

* wraps clear get/setInteger and get/setBoolean

* parse the FMU XML modelDescription on init

* enables xml parser to handle FMU born XML files and adds getValueRef and getCausality methods to FMU wrapper

* wip: get and set FMU state for FMU setRestart and rewindToRestart functionality

* adds convinience method to get and set FMU variables

* wip: adds capability to setRestart and rewindToRestart and adds ex use of rewind in exampleFMU

* cleanup

* ensure GetSetFMUstate capability is enabled by checking FMU XML

* remove debug edits

* clean up unused vars

* read the FMU guid and modelIdentifier directly from the FMU XML if available

* make opaque pointer to fmu obj a property of the FMU_base abstract class

* remove unused FMUv1 specific code

* fix cmake msg status for fmu wrapper config

* adding check for duplicate variables in fmu xml

* fix issue where rewind state does not rewind model time

* retab fmu cpp files

* adding tests for fmu xml parse and fmu model load

* adds test to step fmu model forward and check against expected result

* update license and doc strings

* clean comments in fmu examples

* remove write statement from fmu unit test

* fix doc strings in FMU_Wrapper

* remove extra import c_ptr statements in FMU_f_api

* updates fmu wrapper doc strings and changes fmu interface module name

* Seperate base, Co-Sim and Model Exchange FMU types in the FMU wrapper

* wip: adds initial support for model exchange fmus in the FMU_wrapper

* deduce the number of continuous state variables from the fmu xml file for model exchange fmus

* fixes xml parse issue when a ModelStructure block is present but empty in the FMU XML

* change fmu var get and set interfaces to take character strings rather than StringType

* remove abstract interface for method that is specific to FMIv2 and v1, but not included in v3

* adds additional check to valueReference in FMU_Wrapper unit test

* make properties and methods of the abstract fmu base class private by default

* adds option to auto download the third party FMUs and gold result files in the FMU examples

* adds detailed description of each FMU Wrapper example

* adds rewind consitency check to exampleFMU2_rectifier.f90

* fix whitespace

* fix whitespace in fmu wrapper unittest
parent 13a759f9
......@@ -15,3 +15,31 @@ 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.
---------------------------
Files in the src/fmu_interfaces directory are
Copyright© 2020, Dassault Systemes
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
unless explicitly stated otherwise.
......@@ -7,3 +7,4 @@
# can be found in LICENSE.txt in the head directory of this repository.   !
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++!
ADD_SUBDIRECTORY(exampleTAU_Stubs)
ADD_SUBDIRECTORY(exampleFMU)
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++!
#   Futility Development Group    !
#              All rights reserved.           !
#                         !
# Futility is a jointly-maintained, open-source project between the University !
# of Michigan and Oak Ridge National Laboratory.  The copyright and license !
# can be found in LICENSE.txt in the head directory of this repository.   !
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++!
SET(EXAMPLENAME exampleFMU2_rectifier)
TRIBITS_ADD_EXECUTABLE(${EXAMPLENAME}
SOURCES ${EXAMPLENAME}.f90
LINKER_LANGUAGE Fortran
)
UNSET(EXAMPLENAME)
SET(EXAMPLENAME exampleFMU2_bouncing_ball)
TRIBITS_ADD_EXECUTABLE(${EXAMPLENAME}
SOURCES ${EXAMPLENAME}.f90
LINKER_LANGUAGE Fortran
)
UNSET(EXAMPLENAME)
!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++!
!   Futility Development Group    !
!              All rights reserved.           !
!                         !
! Futility is a jointly-maintained, open-source project between the University !
! of Michigan and Oak Ridge National Laboratory.  The copyright and license !
! can be found in LICENSE.txt in the head directory of this repository.   !
!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++!
!> @brief Program to demo basic FMU model interaction.
!> This example downloads a Co-Simulation FMU model of a bouncing ball from
!> https://github.com/modelica/fmi-cross-check, unpacks the FMU zip archive into
!> a FMU shared object library and an XML model description file, then loads the
!> FMU shared object library and parses the XML model description using the
!> Futility FMU Wrapper. The Futility FMU Wrapper allows one to interact with
!> an FMU model from a fortran program.
!> The loaded FMU is stepped forward in time by 1E-3(s) increments until a
!> final time of 3.0(s) is reached.
!> Results are written to file every 1.0E-2(s) to be consistant with
!> the available gold result files provided by the fmi-cross-check repository
!> for this FMU. The results from the Futility FMU Wrapper are compared to the
!> golden standard results.
!>
!> The model simulates a ball dropped from rest at an initial height of 1(m).
!> The model ignores air resistance and the ball has a coefficient of
!> restitution of 0.7.
!>
!> For information regarding the Functional Mockup Interface (FMI) and
!> Functional Mockup Units (FMU), see https://fmi-standard.org.
!>
!> By default the example program attempts to automatically download the FMU:
!> ./Futility_exampleFMU2_bouncing_ball.exe
!>
!> For manual execution:
!> Download the example third party FMU from the fmi-cross-check repo on github:
!> wget https://github.com/modelica/fmi-cross-check/raw/master/fmus/2.0/cs/linux64/Test-FMUs/0.0.2/BouncingBall/BouncingBall.fmu
!>
!> Benchmark results are provided by:
!> wget https://github.com/modelica/fmi-cross-check/raw/master/fmus/2.0/cs/linux64/Test-FMUs/0.0.2/BouncingBall/BouncingBall_ref.csv
!>
!> Extract the FMU with unzip:
!> unzip BouncingBall.fmu -d /path/to/fmu/BouncingBall
!>
!> Run the example program:
!> ./Futility_exampleFMU2_bouncing_ball.exe /path/to/fmu/BouncingBall BouncingBall_ref.csv
!>
!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++!
PROGRAM testFMU2_BouncingBall
USE Strings
USE IntrType
USE ParameterLists
USE FMU_Wrapper
IMPLICIT NONE
! FMU CS Wrapper
TYPE(FMU2_Slave) :: test_fmu2_cs
! FMU Initilization parameterlist
TYPE(ParamType) :: FMU_params
! Path to FMU unzip directory
CHARACTER(len=256) :: unzipDirectory
! Path to gold result file to benchmark against
CHARACTER(len=256) :: goldFile
! ID of fmu used for bookeeping
INTEGER(SIK) :: fmu_id=1_SIK
! Time step size
REAL(SRK) :: dt=1.0E-3_SRK
! Start and end of simulation time
REAL(SRK) :: timeStart=0.0_SRK
REAL(SRK) :: timeEnd=3.0_SRK
! FMU ODE solver tolerance
REAL(SRK) :: tol=1.0E-5_SRK
! Local time storage
REAL(SRK) :: time=0.0_SRK
REAL(SRK) :: write_time=0.0_SRK
! Variable to store ball height and velocity
REAL(SRK) :: ball_velocity, ball_height
TYPE(StringType) :: varName
INTEGER(SIK) :: i, imax
! Temporary variable storage
REAL(SRK),ALLOCATABLE :: t_calc(:)
REAL(SRK),ALLOCATABLE :: h_calc(:)
REAL(SRK),ALLOCATABLE :: v_calc(:)
REAL(SRK),ALLOCATABLE :: t_gold(:)
REAL(SRK),ALLOCATABLE :: h_gold(:)
REAL(SRK),ALLOCATABLE :: v_gold(:)
! Example FMU parameter settings
CALL FMU_params%clear()
CALL FMU_params%add('FMU_Wrapper->id',fmu_id)
IF (IARGC()==2) THEN
CALL getarg(1, unzipDirectory)
CALL getarg(2, goldFile)
ELSE
WRITE(*,*) "WARNING: Attempting to download the FMU from the internet."
! Optionally download the third party FMU from the internet.
CALL SYSTEM("wget -N https://github.com/modelica/fmi-cross-check/raw/master/fmus/2.0/cs/linux64/Test-FMUs/0.0.2/BouncingBall/BouncingBall.fmu")
CALL SYSTEM("unzip -o BouncingBall.fmu -d BouncingBallFmu_unzipDir")
unzipDirectory = "./BouncingBallFmu_unzipDir"
CALL SYSTEM("wget -N https://github.com/modelica/fmi-cross-check/raw/master/fmus/2.0/cs/linux64/Test-FMUs/0.0.2/BouncingBall/BouncingBall_ref.csv")
goldFile = "BouncingBall_ref.csv"
! Fix the extracted XML file format
CALL SYSTEM("sed -i 's/ISO-8859-1/UTF-8/g' ./BouncingBallFmu_unzipDir/modelDescription.xml")
ENDIF
CALL FMU_params%add('FMU_Wrapper->unzipDirectory', trim(unzipDirectory))
! Init the FMU_Wrapper
CALL test_fmu2_cs%init(fmu_id, FMU_params)
CALL test_fmu2_cs%setupExperiment(.TRUE., tol, timeStart, .TRUE., timeEnd)
! Ensure that desired variables are present in the FMU
varName='g'
IF(test_fmu2_cs%isXmlVar(CHAR(varName))) &
WRITE(*,*) "FMU variable: ", CHAR(varName), " has causality: ", &
CHAR(test_fmu2_cs%getCausality(CHAR(varName)))
varName='e'
IF(test_fmu2_cs%isXmlVar(CHAR(varName))) &
WRITE(*,*) "FMU variable: ", CHAR(varName), " has causality: ", &
CHAR(test_fmu2_cs%getCausality(CHAR(varName)))
varName='h'
IF(test_fmu2_cs%isXmlVar(CHAR(varName))) &
WRITE(*,*) "FMU variable: ", CHAR(varName), " has causality: ", &
CHAR(test_fmu2_cs%getCausality(CHAR(varName)))
! set gravity acceleration parameter
CALL test_fmu2_cs%setNamedVariable('g', -9.81_SRK)
! set the coefficient of restitution
CALL test_fmu2_cs%setNamedVariable('e', 0.7_SRK)
! Open an output file
OPEN(unit=42, file="exampleFMU2_bouncing_ball_out.csv")
! Write output header
WRITE(*,*) "Writing results to exampleFMU2_bouncing_ball_out.csv"
WRITE(42,*) "time[s], height[m], velocity[m/s]"
! Step the CS FMU model forward in time
imax=0_SIK
write_time=0.0_SRK
DO
! Get the variable values of interest from the FMU
CALL test_fmu2_cs%getNamedVariable('v', ball_velocity)
CALL test_fmu2_cs%getNamedVariable('h', ball_height)
! Print the result to stdout
IF(ABS(write_time - 0.01_SRK) < 1.0e-8_SRK .OR. time <= 1.0e-16_SRK) THEN
WRITE(42,*) time, ball_height, ball_velocity
write_time = 0.0_SRK
imax = imax + 1
ENDIF
time = time + dt
write_time = write_time + dt
IF(time >= timeEnd) EXIT
! Step the FMU forward
CALL test_fmu2_cs%doStep(dt)
ENDDO
CLOSE(unit=42)
! Check result against fmi-cross-check benchmark values
WRITE(*,*) "Checking results against BouncingBall_ref.csv"
WRITE(*,'(A15,A15,A15,A15)') "Time(s)", "Calc Ball H(m)", "Gold Ball H(m)", "Diff"
OPEN(unit=42, file="exampleFMU2_bouncing_ball_out.csv")
OPEN(unit=43, file=goldFile)
ALLOCATE(t_calc(imax))
ALLOCATE(h_calc(imax))
ALLOCATE(v_calc(imax))
ALLOCATE(t_gold(imax))
ALLOCATE(h_gold(imax))
ALLOCATE(v_gold(imax))
DO i=0,imax
! skip the header
IF(i==0) THEN
READ(42,*)
READ(43,*)
ELSE
READ(42,*) t_calc(i), h_calc(i), v_calc(i)
READ(43,*) t_gold(i), h_gold(i), v_gold(i)
WRITE(*,'(F15.7,ES15.7,ES15.7,ES15.7)') t_calc(i), h_calc(i), h_gold(i), h_calc(i)-h_gold(i)
ENDIF
ENDDO
CLOSE(unit=42)
CLOSE(unit=43)
! Clean up
CALL test_fmu2_cs%clear()
CALL FMU_params%clear()
DEALLOCATE(t_calc)
DEALLOCATE(h_calc)
DEALLOCATE(v_calc)
DEALLOCATE(t_gold)
DEALLOCATE(h_gold)
DEALLOCATE(v_gold)
ENDPROGRAM testFMU2_BouncingBall
!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++!
!   Futility Development Group    !
!              All rights reserved.           !
!                         !
! Futility is a jointly-maintained, open-source project between the University !
! of Michigan and Oak Ridge National Laboratory.  The copyright and license !
! can be found in LICENSE.txt in the head directory of this repository.   !
!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++!
!> @brief Program to demo basic FMU model interaction.
!> This example downloads a Co-Simulation FMU model of a rectifier circuit from
!> https://github.com/modelica/fmi-cross-check, unpacks the FMU zip archive into
!> a FMU shared object library and an XML model description file, then loads the
!> FMU shared object library and parses the XML model description using the
!> Futility FMU Wrapper. The loaded FMU is stepped forward in time by 1E-7(s)
!> increments until a final time of 0.1(s) is reached.
!> Results are written to file every 2.0E-4(s) to be consistant with
!> the available gold result files provided by the fmi-cross-check repository
!> for this FMU. The results from the Futility FMU Wrapper are compared to the
!> golden standard results. Finally, to test the rewind capability of the FMU
!> Wrapper, a restart point is set at 0.1(s), the solutin is stepped forward by
!> 10 time steps, then the FMU is rewound to the restart point and the same 10
!> time steps are performed again. The results from the 10 steps before the
!> rewind are compared against the 10 steps following the rewind.
!>
!> For information regarding the Functional Mockup Interface (FMI) and
!> Functional Mockup Units (FMU), see https://fmi-standard.org.
!>
!> By default the program attempts to automatically download the FMU:
!> ./Futility_exampleFMU2_rectifier.exe
!>
!> For manual execution:
!> Download the example third party FMU from the fmi-cross-check repo on github:
!> wget https://github.com/modelica/fmi-cross-check/raw/master/fmus/2.0/cs/linux64/MapleSim/2016.2/Rectifier/Rectifier.fmu
!>
!> Benchmark results are provided by:
!> wget https://github.com/modelica/fmi-cross-check/raw/master/fmus/2.0/cs/linux64/MapleSim/2016.2/Rectifier/Rectifier_ref.csv
!>
!> Extract the FMU with unzip:
!> unzip Rectifier.fmu -d /path/to/fmu/rectifier_example_fmu
!>
!> Run the example program:
!> ./Futility_exampleFMU2_rectifier.exe /path/to/fmu/rectifier_example_fmu Recitfier_ref.csv
!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++!
PROGRAM testFMU2
USE Strings
USE IntrType
USE ParameterLists
USE FMU_Wrapper
IMPLICIT NONE
TYPE(FMU2_Slave) :: test_fmu2_cs
TYPE(ParamType) :: FMU_params
CHARACTER(len=256) :: unzipDirectory
CHARACTER(len=256) :: goldFile
INTEGER(SIK) :: id=3_SIK
REAL(SRK) :: h=1.0E-7_SRK
REAL(SRK) :: timeStart=0.0_SRK
REAL(SRK) :: timeEnd=1.5E-1_SRK
REAL(SRK) :: tol=1.0E-9_SRK
REAL(SRK) :: time, voltage1
REAL(SRK) :: write_time=0.0_SRK
REAL(SRK) :: pre_rewind_t(10)
REAL(SRK) :: pre_rewind_v(10)
REAL(SRK) :: post_rewind_t(10)
REAL(SRK) :: post_rewind_v(10)
INTEGER(SIK) :: i
! Example FMU parameter settings
CALL FMU_params%clear()
CALL FMU_params%add('FMU_Wrapper->id',id)
IF (IARGC() == 2) THEN
CALL getarg(1, unzipDirectory)
CALL getarg(2, goldFile)
ELSE
WRITE(*,*) "WARNING: Attempting to download the FMU from the internet."
! Optionally download the third party FMU from the internet.
CALL SYSTEM("wget -N https://github.com/modelica/fmi-cross-check/raw/master/fmus/2.0/cs/linux64/MapleSim/2016.2/Rectifier/Rectifier.fmu")
CALL SYSTEM("unzip -o Rectifier.fmu -d Rectifier_unzipDir")
unzipDirectory = "./Rectifier_unzipDir"
CALL SYSTEM("wget -N https://github.com/modelica/fmi-cross-check/raw/master/fmus/2.0/cs/linux64/MapleSim/2016.2/Rectifier/Rectifier_ref.csv")
goldFile = "Rectifier_ref.csv"
! Fix the extracted XML file format
CALL SYSTEM("sed -i 's/ISO-8859-1/UTF-8/g' ./Rectifier_unzipDir/modelDescription.xml")
ENDIF
CALL FMU_params%add('FMU_Wrapper->unzipDirectory', trim(unzipDirectory))
WRITE(*,*) '==================================================='
WRITE(*,*) 'TESTING FMU2...'
WRITE(*,*) '==================================================='
! Init the FMU_Wrapper
CALL test_fmu2_cs%init(id, FMU_params)
CALL test_fmu2_cs%setupExperiment(.FALSE., tol, timeStart, .TRUE., timeEnd)
! Open an output file
OPEN(unit=42, file="exampleFMU2_rectifier_out.csv")
! Write output header
WRITE(*,*) "Writing results to exampleFMU2_rectifier_out.csv"
WRITE(42,*) "time[s], voltage1[v]"
! Step the FMU forward
DO
CALL test_fmu2_cs%getReal(0, time)
CALL test_fmu2_cs%getReal(1, voltage1)
CALL test_fmu2_cs%doStep(h)
IF(ABS(write_time - 0.0002_SRK) < 1.0e-8_SRK) THEN
WRITE(42,*) time, voltage1
write_time = 0.0_SRK
ENDIF
write_time = write_time + h
IF(time >= timeEnd-0.5E-1_SRK) EXIT
ENDDO
CLOSE(unit=42)
! get valueReference to variables and causality
WRITE(*,*) "internalTime", " valueReference: ", test_fmu2_cs%getValueReference("internalTime"), &
" causality: ", CHAR(test_fmu2_cs%getCausality("internalTime"))
WRITE(*,*) "outputs", " valueReference: ", test_fmu2_cs%getValueReference("outputs"), &
" causality: ", CHAR(test_fmu2_cs%getCausality("outputs"))
! set restart point
CALL test_fmu2_cs%setRestart()
WRITE(*,*) "Set restart"
DO i=1,10
CALL test_fmu2_cs%getReal(0, time)
CALL test_fmu2_cs%getReal(1, voltage1)
CALL test_fmu2_cs%doStep(h)
pre_rewind_t(i) = time
pre_rewind_v(i) = voltage1
ENDDO
! rewind to restart
CALL test_fmu2_cs%rewindToRestart()
WRITE(*,*) "Rewind to restart"
DO i=1,10
CALL test_fmu2_cs%getReal(0, time)
CALL test_fmu2_cs%getReal(1, voltage1)
CALL test_fmu2_cs%doStep(h)
post_rewind_t(i) = time
post_rewind_v(i) = voltage1
ENDDO
WRITE(*,'(A15,A15,A15,A15,A15)') "Pre RW T(s)", "Post RW T(s)", "Pre RW (V)", "Post RW (V)", "Diff"
DO i=1,10
WRITE(*,'(F15.7,F15.7,F15.7,F15.7,E15.7)') pre_rewind_t(i), post_rewind_t(i), pre_rewind_v(i), &
post_rewind_v(i), post_rewind_v(i)-pre_rewind_v(i)
ENDDO
! get named variables
CALL test_fmu2_cs%getNamedVariable("internalTime", time)
WRITE(*,*) "final time: ", time
! Clean up
CALL test_fmu2_cs%clear()
CALL FMU_params%clear()
ENDPROGRAM testFMU2
......@@ -20,6 +20,7 @@ TRIBITS_ADD_LIBRARY(CUtils
)
ADD_SUBDIRECTORY(trilinos_interfaces)
ADD_SUBDIRECTORY(fmu_interfaces)
#Library for utility code
TRIBITS_ADD_LIBRARY(Utils
......@@ -41,6 +42,7 @@ TRIBITS_ADD_LIBRARY(Utils
BLAS2.f90
BLAS3.f90
BLAS.f90
FMU_Wrapper.f90
trilinos_interfaces/trilinos_f_interfaces.f90
Strings.f90
Times.f90
......@@ -105,5 +107,6 @@ TRIBITS_ADD_LIBRARY(Utils
DEPLIBS
CUtils
TrilinosUtils
FmuUtils
)
This diff is collapsed.
......@@ -1260,8 +1260,9 @@ SUBROUTINE processTagAttributes(elStartTag,nattr,anames,avalues,ierr)
CHARACTER(LEN=1) :: quote
CHARACTER(LEN=LEN(elStartTag)) :: startTag
INTEGER(SIK) :: ic,i,nchars,namestt,valstt,valstp
INTEGER(SIK) :: ic,i,nchars,namestt,valstt,valstp,bs_i
INTEGER(SIK),ALLOCATABLE :: anchorLoc(:)
LOGICAL(SBK) :: word_encountered
!Initialize return arguments
ierr=0
......@@ -1308,11 +1309,27 @@ SUBROUTINE processTagAttributes(elStartTag,nattr,anames,avalues,ierr)
ENDDO
ENDIF
!Get the names (names precede the '=' character with no whitespace)
!Get the names (names precede the '=' character with possible whitespace preceeding '=')
!attribute names cannot contain whitespace, must be unique and preceded
!by whitespace
DO i=1,nattr
namestt=SCAN(startTag(1:anchorLoc(i)),' '//CHAR(9)//CHAR(10)//CHAR(13),.TRUE.)
! March backwards from anchorLoc(i) through char array if whitespace preceeds the '='
IF(startTag(anchorLoc(i)-1:anchorLoc(i)-1) == ' ') THEN
! march through word string backwards until whitespace is encountered
word_encountered=.FALSE.
DO bs_i=anchorLoc(i)-2,1,-1
IF(startTag(bs_i:bs_i) /= ' ' .AND. .NOT. word_encountered) word_encountered=.TRUE.
IF(startTag(bs_i:bs_i) == ' ' .AND. word_encountered) THEN
namestt=bs_i
EXIT
ENDIF
IF(bs_i==1) ierr=-3
ENDDO
ELSE
! CHAR(9) == TAB, CHAR(10) == Newline, CHAR(13) == Carriage return
! SCAN(...,TRUE) returns rightmost position
namestt=SCAN(startTag(1:anchorLoc(i)),' '//CHAR(9)//CHAR(10)//CHAR(13),.TRUE.)
ENDIF
IF(0 < namestt .AND. namestt < anchorLoc(i)) THEN
anames(i)=startTag(namestt+1:anchorLoc(i)-1)
ELSE
......@@ -1323,6 +1340,17 @@ SUBROUTINE processTagAttributes(elStartTag,nattr,anames,avalues,ierr)
!Get the values
DO i=1,nattr
valstt=anchorLoc(i)+1
! In case of whitespace following '='
IF(startTag(valstt:valstt)==' ') THEN
! march forward until quote is encountered
DO bs_i=anchorLoc(i)+1,nchars
IF(startTag(bs_i:bs_i) == '"') THEN
valstt=bs_i
EXIT
ENDIF
IF(bs_i==nchars) ierr=-4
ENDDO
ENDIF
quote=startTag(valstt:valstt)
valstp=INDEX(startTag(valstt+1:nchars),quote)+valstt
IF(valstt < valstp) THEN
......
......@@ -533,6 +533,7 @@ TYPE :: ParamType
PROCEDURE,PASS :: toTeuchosPlist
#endif
PROCEDURE :: procXMLTree
PROCEDURE :: procFMUXMLTree
ENDTYPE ParamType
!> @brief Wrapper type for an array of ParamType pointers
......@@ -11171,14 +11172,118 @@ RECURSIVE SUBROUTINE procXMLTree(thisParam,parent,currentPath)
ENDSUBROUTINE procXMLTree
!
!-------------------------------------------------------------------------------
SUBROUTINE initFromXML(thisParam, fname)
RECURSIVE SUBROUTINE procFMUXMLTree(thisParam,parent,currentPath)
CLASS(ParamType),INTENT(INOUT) :: thisParam
TYPE(StringType),INTENT(IN) :: currentPath
TYPE(XMLElementType),POINTER :: iXMLE,children(:),dChildren(:),parent
TYPE(ParamType),POINTER :: pList(:)
TYPE(StringType) :: elname,tmpPath,tmpNewPath
INTEGER(SIK) :: ic,ia,ib
TYPE(StringType),ALLOCATABLE :: tmpKeys(:)
TYPE(StringType),ALLOCATABLE :: tmpValues(:)
TYPE(StringType) :: tmpKey, tmpVal, tmpPathToTmpVar
NULLIFY(pList)
CALL parent%getChildren(children)
!Check to see if it's an empty parameter list
IF(.NOT.ASSOCIATED(children)) THEN
ALLOCATE(pList(0))
CALL thisParam%add(CHAR(currentPath),pList)
DEALLOCATE(pList)
RETURN
ENDIF
DO ic=1,SIZE(children)
tmpPath=currentPath//' -> '
iXMLE => children(ic)
elname=iXMLE%name%upper()
tmpNewPath = tmpPath // elname
IF(elname == 'SCALARVARIABLE') THEN
CALL iXMLE%getAttributes(tmpKeys, tmpValues)
DO ia=1,SIZE(tmpKeys)
tmpKey = tmpKeys(ia)
tmpVal = tmpValues(ia)
IF(tmpKey=='name') THEN
DO ib=1,SIZE(tmpKeys)
IF(tmpKeys(ib)=='valueReference') THEN
tmpPathToTmpVar = 'FMU'//currentPath//' -> '//tmpVal//' -> valueReference'
IF(thisParam%has(CHAR(tmpPathToTmpVar))) THEN
CALL eParams%raiseWarning(modName//" - Duplicate FMU Variable: "//CHAR(tmpPathToTmpVar))
CALL thisParam%set(CHAR(tmpPathToTmpVar),tmpValues(ib))
ELSE
CALL thisParam%add(CHAR(tmpPathToTmpVar),tmpValues(ib))
ENDIF
ELSE IF(tmpKeys(ib)=='causality') THEN
tmpPathToTmpVar = 'FMU'//currentPath//' -> '//tmpVal//' -> causality'
CALL thisParam%add(CHAR(tmpPathToTmpVar),tmpValues(ib))
ENDIF
ENDDO
ENDIF
ENDDO
DEALLOCATE(tmpKeys)
DEALLOCATE(tmpValues)
ELSE IF(elname == 'DEFAULTEXPERIMENT') THEN
CALL iXMLE%getAttributes(tmpKeys, tmpValues)
DO ia=1,SIZE(tmpKeys)
tmpPathToTmpVar = elname//currentPath//' -> '//tmpKeys(ia)
CALL thisParam%add(CHAR(tmpPathToTmpVar),tmpValues(ia))
ENDDO
ELSE IF(elname == 'COSIMULATION') THEN
CALL iXMLE%getAttributes(tmpKeys, tmpValues)
DO ia=1,SIZE(tmpKeys)
tmpPathToTmpVar = elname//currentPath//' -> '//tmpKeys(ia)
CALL thisParam%add(CHAR(tmpPathToTmpVar),tmpValues(ia))
ENDDO
ELSE IF(elname == 'MODELVARIABLES') THEN
CALL procFMUXMLTree(thisParam,iXMLE,tmpNewPath)
ELSE IF(elname == 'MODELSTRUCTURE') THEN