stardis

Perform coupled heat transfer calculations
git clone git://git.meso-star.fr/stardis.git
Log | Files | Refs | README | LICENSE

commit 42824c2474a58cbcd24e72fefd685013a62f022d
parent 124f1ba33703524a312711f844859e4f012ac394
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Thu, 19 Nov 2020 17:58:47 +0100

Merge branch 'release_0.5'

Diffstat:
A.gitignore | 11+++++++++++
MREADME.md | 104+++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Mcmake/CMakeLists.txt | 164+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Acmake/doc/CMakeLists.txt | 149+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adoc/stardis-input.5.txt | 207+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adoc/stardis-man.css | 96+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adoc/stardis-output.5.txt | 942+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adoc/stardis.1.txt.in | 320+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dpp/green2xslx.pl | 567-------------------------------------------------------------------------------
Dsrc/args.h | 373-------------------------------------------------------------------------------
Dsrc/b_T_1.stl | 16----------------
Dsrc/b_T_2.stl | 30------------------------------
Dsrc/b_h_1.stl | 156-------------------------------------------------------------------------------
Dsrc/bc.txt | 5-----
Dsrc/m_1.stl | 114-------------------------------------------------------------------------------
Dsrc/m_2.stl | 86-------------------------------------------------------------------------------
Dsrc/m_3.stl | 86-------------------------------------------------------------------------------
Dsrc/main.c | 48------------------------------------------------
Dsrc/material.txt | 4----
Msrc/stardis-app.c | 1476++++++++++++++++++++++++++++---------------------------------------------------
Msrc/stardis-app.h | 1465+++++++++++++++++++++++++++++++++----------------------------------------------
Msrc/stardis-compute.c | 2754++++++++++++++++++++++++++-----------------------------------------------------
Asrc/stardis-compute.h | 44++++++++++++++++++++++++++++++++++++++++++++
Asrc/stardis-default.h.in | 37+++++++++++++++++++++++++++++++++++++
Asrc/stardis-fluid.c | 164+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stardis-fluid.h | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stardis-intface.c | 364+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stardis-intface.h | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stardis-main.c | 125+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stardis-output.c | 1727+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stardis-output.h | 110+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stardis-parsing.c | 1820+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stardis-parsing.h | 203+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stardis-solid.c | 221+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stardis-solid.h | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stardis-version.h.in | 24++++++++++++++++++++++++
Dtinyexpr/tinyexpr.c | 890-------------------------------------------------------------------------------
Dtinyexpr/tinyexpr.h | 197-------------------------------------------------------------------------------
38 files changed, 8957 insertions(+), 6328 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,11 @@ +.gitignore +CMakeCache.txt +CMakeFiles +Makefile +tmp +[Bb]uild* +*.sw[po] +*.[ao] +*~ +tags + diff --git a/README.md b/README.md @@ -1,58 +1,81 @@ -#stardis-app : Prototype d'application Stardis +# stardis -stardis-app est un prototype d'applicatif mettant en oeuvre la bibliothèque -stardis-solver (https://gitlab.com/meso-star/stardis-solver) avec comme objectif -de faire la jonction entre un outil de CAO telle que Salome et le solver. +## Overview -Pour compiler il suffit de taper `make` après avoir préalablement renseigner -dans le makefile la variable SDIS_SDK qui est le chemin du SDK de Stardis. Voir -les notes de version en ci-dessous pour avoir la bonne version du SDK. +Stardis is a software dedicated to the resolution of coupled +convective-conductive-radiative thermal problems in 3D environments. +It is based on [stardis-solver](https://gitlab.com/meso-star/stardis-solver) +and exposes some of the main features of the solver in an easy to use way. +Using stardis is a practical way of carrying out thermal studies on CAD +models which can be exported from Salomé or other similar software. -# Note de versions +## How to build -## v0.4 +Stardis relies on the [CMake](http://www.cmake.org) and the +[RCMake](https://gitlab.com/vaplv/rcmake/) package to build. +It also depends on the +[RSys](https://gitlab.com/vaplv/rsys/), +[star-3d](https://gitlab.com/meso-star/star-3d/), +[star-enclosures-3d](https://gitlab.com/meso-star/star-enclosures-3d/), +[star-geometry-3d](https://gitlab.com/meso-star/star-geometry-3d/), +[star-stl](https://gitlab.com/meso-star/star-stl) and +[stardis-solver](https://gitlab.com/meso-star/stardis-solver) libraries +as well as on the [OpenMP](http://www.openmp.org) 2.0 specification to +parallelize its computations. -- Conformité C99. -- Build Windows. -- Passage à stardis-solver 0.5 ; l'intégration temporelle n'est toutefois pas - encore accessible. -- Passage à cmake -## v0.3.2 +First ensure that CMake and a C99 compiler that implements the OpenMP 2.0 +specification are installed on your system. Then install the RCMake package as +well as all the aforementioned prerequisites. Finally generate the project from +the `cmake/CMakeLists.txt` file by appending to the `CMAKE_PREFIX_PATH` +variable the install directories of its dependencies. -- Ajout de la fonctionnalité solve_probe_boundary. La fonction solve est - automatiquement selectionnée en fonction de la distance à la primitive la plus - proche. Si la distance est inférieure à 2.1 delta, la fonction - solve_probe_boundary sera utilisée. -- Ajout des conditions limites en flux. +## Note de versions -## v0.3.1 +### v0.5 -Ajout du tranfert radiatif. Ce qui ajoute deux nouveaux parametrès par medium : +- Ensure C89 compliance. +- New output format for infra-red rendering. +- Use new stardis-solver 0.11. +- Replace fixed dates by time-ranges as time arguments + for computations. +- Allow unsteady Green's function computations. +- Model files now include scale parameter. + +### v0.4 + +- Improve C99 compliance. +- Build on Windows systems. +- Use new stardis-solver 0.5. +- Transition to cmake to manage builds. + +### v0.3.2 + +- Add the solve_probe_boundary feature. The solve_probe_boundary VS + solve_probe selection is automated according the probe-geometry distance. + solve_probe_boundary is called for probe points closer than 2.1 delta + from geometry. +- Add flux boundary conditions. + +### v0.3.1 + +Add radiative transfer computations. To achieve this, media gain 2 new parameters: - emissivity; - specular_fraction. -## v0.3 - -Passage à la version 0.3.0 du SDK de stardis-solver. La version modifié du SDK -est disponible sur le NAS -(meso-star/repo/Stardis-0.1.0-GNU-Linux64_modified.tar.gz). Trois fonctionnalités -ont été ajoutés : +### v0.3 - - la puissance volumique; - - la possibilité de renseigner l'argument fp_to_meter de la fonction solve de - stardis-solver; - - une option pour dumper la géométrie après analyse au format VTK avec des tags - medium front/back et id des conditions limites. +- Upgrade stardis-solver to v0.3. +- Add volumic power sources on solids; +- Allow to use the fp_to_meter parameter of the stardis-solver solve function; +- Add a dump geometry feature. It outputs the geometry as it is sent to + stardis-solver in VTK format, together with the front and back media and + boundary conditions information. -## v0.1 +### v0.1 -Evalue une température sonde uniquement, pour un système purement conductif.Les -conditions aux limites peuvent être de type dirichlet ou de type échange. Cette -version repose sur le SDK de Stardis modifié 0.1.0 disponible sur la NAS -(meso-star/repo/Stardis-0.1.0-GNU-Linux64_modified.tar.gz). La modification -consiste en l'ajout de la bibliothèqe sstl et tinyexpr -(https://github.com/codeplea/tinyexpr (https://github.com/codeplea/tinyexpr) +- Allow probe computations on conductive-only thermal systems. +- Allow Dirichlet and h.dT boundary conditions. +\ No newline at end of file diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2018 |Meso|Star> +# Copyright (C) 2018-2020 |Meso|Star> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -14,29 +14,80 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. cmake_minimum_required(VERSION 3.0) -project(stardis-app C) +project(stardis C) set(SDIS_SOURCE_DIR ${PROJECT_SOURCE_DIR}/../src) -################################################################################ +if(CMAKE_HOST_UNIX) + set(STARDIS_DOC "TROFF" CACHE STRING + "Type of documentation to generate and install.") +else() + set(STARDIS_DOC "HTML" CACHE STRING + "Type of documentation to generate and install.") +endif() + +set_property(CACHE STARDIS_DOC PROPERTY STRINGS + "HTML" + "TROFF" + "TROFF & HTML" + "NONE") + +############################################################################### +# Generate files +############################################################################### +set(STARDIS_ARGS_DEFAULT_AMBIENT_TEMP "300") +set(STARDIS_ARGS_DEFAULT_COMPUTE_TIME "INF, INF") +set(STARDIS_ARGS_DEFAULT_RENDERING_FOV "70") # degrees +set(STARDIS_ARGS_DEFAULT_RENDERING_IMG_HEIGHT "480") +set(STARDIS_ARGS_DEFAULT_RENDERING_IMG_WIDTH "640") +set(STARDIS_ARGS_DEFAULT_RENDERING_OUTPUT_FILE_FMT "HT") # VTK or HT +set(STARDIS_ARGS_DEFAULT_RENDERING_POS "1, 1, 1") +set(STARDIS_ARGS_DEFAULT_RENDERING_SPP "4") +set(STARDIS_ARGS_DEFAULT_RENDERING_TGT "0, 0, 0") +set(STARDIS_ARGS_DEFAULT_RENDERING_TIME "INF, INF") +set(STARDIS_ARGS_DEFAULT_RENDERING_UP "0, 0, 1") +set(STARDIS_ARGS_DEFAULT_REFERENCE_TEMP "300") +set(STARDIS_ARGS_DEFAULT_SAMPLES_COUNT "10000") +set(STARDIS_ARGS_DEFAULT_SCALE_FACTOR "1") +set(STARDIS_ARGS_DEFAULT_VERBOSE_LEVEL "1") + +configure_file(${SDIS_SOURCE_DIR}/../doc/stardis.1.txt.in + ${CMAKE_CURRENT_BINARY_DIR}/doc/stardis.1.txt @ONLY) + +set(SDIS_VERSION_MAJOR 0) +set(SDIS_VERSION_MINOR 5) +set(SDIS_VERSION_PATCH 0) +set(SDIS_VERSION ${SDIS_VERSION_MAJOR}.${SDIS_VERSION_MINOR}.${SDIS_VERSION_PATCH}) + +configure_file(${SDIS_SOURCE_DIR}/stardis-default.h.in + ${CMAKE_CURRENT_BINARY_DIR}/stardis-default.h @ONLY) + +configure_file(${SDIS_SOURCE_DIR}/stardis-version.h.in + ${CMAKE_CURRENT_BINARY_DIR}/stardis-version.h @ONLY) + +############################################################################### # Check dependencies -################################################################################ +############################################################################### find_package(RCMake 0.4 REQUIRED) -find_package(RSys 0.8.1 REQUIRED) -find_package(StarEnc 0.3.1 REQUIRED) -find_package(Stardis 0.7.1 REQUIRED) +find_package(RSys 0.11 REQUIRED) +find_package(StarGeom3D 0.1 REQUIRED) +find_package(Star3D 0.7.1 REQUIRED) +find_package(StarEnc3D 0.4.2 REQUIRED) +find_package(Stardis 0.11 REQUIRED) find_package(StarSTL 0.3 REQUIRED) - -set(TINYEXPR_SOURCE_DIR "${PROJECT_SOURCE_DIR}/../tinyexpr" CACHE PATH "Directory for TinyExpr source files") +if(MSVC) + find_package(MuslGetopt REQUIRED) +endif() include_directories( ${RSys_INCLUDE_DIR} - ${StarEnc_INCLUDE_DIR} + ${Star3D_INCLUDE_DIR} + ${StarGeom3D_INCLUDE_DIR} + ${StarEnc3D_INCLUDE_DIR} ${Stardis_INCLUDE_DIR} ${StarSTL_INCLUDE_DIR} - ${TINYEXPR_SOURCE_DIR}) + ${CMAKE_CURRENT_BINARY_DIR}) if(MSVC) - find_package(MuslGetopt REQUIRED) include_directories(${MuslGetopt_INCLUDE_DIR}) endif() @@ -44,24 +95,45 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${RCMAKE_SOURCE_DIR}) include(rcmake) include(rcmake_runtime) -rcmake_append_runtime_dirs(_runtime_dirs RSys Stardis StarEnc StarSTL) +if(CMAKE_COMPILER_IS_GNUCC) + rcmake_append_runtime_dirs(_runtime_dirs + RSys Stardis Star3D StarGeom3D StarEnc3D StarSTL) +endif() +if(MSVC) + rcmake_append_runtime_dirs(_runtime_dirs + RSys MuslGetopt Stardis Star3D StarGeom3D StarEnc3D StarSTL) +endif() + +############################################################################### +# Build subprojects +############################################################################### +if(NOT STARDIS_DOC STREQUAL "NONE") + add_subdirectory(doc) +endif() -################################################################################ +############################################################################### # Configure and define targets -################################################################################ -set(VERSION_MAJOR 0) -set(VERSION_MINOR 4) -set(VERSION_PATCH 0) -set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) - +############################################################################### set(SDIS_FILES_SRC stardis-app.c stardis-compute.c - main.c) + stardis-fluid.c + stardis-intface.c + stardis-main.c + stardis-output.c + stardis-parsing.c + stardis-solid.c) set(SDIS_FILES_INC stardis-app.h - args.h) + stardis-compute.h + stardis-default.h.in + stardis-fluid.h + stardis-intface.h + stardis-output.h + stardis-parsing.h + stardis-solid.h + stardis-version.h.in) set(SDIS_FILES_DOC COPYING README.md) @@ -70,46 +142,30 @@ rcmake_prepend_path(SDIS_FILES_SRC ${SDIS_SOURCE_DIR}) rcmake_prepend_path(SDIS_FILES_INC ${SDIS_SOURCE_DIR}) rcmake_prepend_path(SDIS_FILES_DOC ${PROJECT_SOURCE_DIR}/../) -# Default is right to left pow and log is natural log -# Build tinyExpr without closure support and with function_1 to function_3 support only -# (these ones cannot be disabled as predefined functions require them). -set(TE_DEFAULTS - "-DTE_POW_FROM_RIGHT -DTE_NAT_LOG -DTE_WITHOUT_CLOSURES -DTE_WITHOUT_FUNCTION_0 -DTE_MAX_FUNCTION_ARITY=3") - -ADD_LIBRARY(tinyexpr STATIC - ${TINYEXPR_SOURCE_DIR}/tinyexpr.c ${TINYEXPR_SOURCE_DIR}/tinyexpr.h) - -if(CMAKE_COMPILER_IS_GNUCC) - set_target_properties(tinyexpr PROPERTIES - COMPILE_FLAGS "-std=c99 ${TE_DEFAULTS}") -elseif(MSVC) - set_target_properties(tinyexpr PROPERTIES - COMPILE_FLAGS ${TE_DEFAULTS}) -endif() - -add_executable(sdis-app +add_executable(stardis ${SDIS_FILES_SRC} ${SDIS_FILES_INC}) - + if(CMAKE_COMPILER_IS_GNUCC) - set_target_properties(sdis-app PROPERTIES - COMPILE_FLAGS "-std=c99 ${TE_DEFAULTS}" - VERSION ${VERSION}) - target_link_libraries(sdis-app Stardis StarEnc StarSTL RSys tinyexpr m) + set(MATH_LIB m) elseif(MSVC) - set_target_properties(sdis-app PROPERTIES - COMPILE_FLAGS "${TE_DEFAULTS}" - VERSION ${VERSION}) - target_link_libraries(sdis-app Stardis StarEnc StarSTL RSys tinyexpr MuslGetopt) + set(GETOPT_LIB MuslGetopt) endif() -rcmake_copy_runtime_libraries(sdis-app) +set_target_properties(stardis + PROPERTIES VERSION ${SDIS_VERSION}) -################################################################################ +target_link_libraries(stardis + Stardis Star3D StarGeom3D StarEnc3D StarSTL RSys ${GETOPT_LIB} ${MATH_LIB}) + +############################################################################### # Define output & install directories -################################################################################ -install(TARGETS sdis-app +############################################################################### +install(TARGETS stardis ARCHIVE DESTINATION bin LIBRARY DESTINATION lib RUNTIME DESTINATION bin) -install(FILES ${SDIS_FILES_DOC} DESTINATION share/doc/stardis-app) + +install(FILES ${SDIS_FILES_DOC} DESTINATION share/doc/stardis) + +rcmake_copy_runtime_libraries(stardis) diff --git a/cmake/doc/CMakeLists.txt b/cmake/doc/CMakeLists.txt @@ -0,0 +1,149 @@ +# Copyright (C) 2018-2020 |Meso|Star> +# +# This program 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. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + +cmake_minimum_required(VERSION 3.0) + +string(REGEX MATCH ".*HTML.*" _html ${STARDIS_DOC}) +string(REGEX MATCH ".*ROFF.*" _roff ${STARDIS_DOC}) + +set(STARDIS_DOC_DIR ${PROJECT_SOURCE_DIR}/../doc) + +################################################################################ +# Look for asciidoc and a2x programs +################################################################################ +if(_html) + find_program(ASCIIDOC NAMES asciidoc asciidoc.py) + if(NOT ASCIIDOC) + unset(_html) + message(WARNING + "The `asciidoc' program is missing. " + "The stardis HTML documentation cannot be generated.") + endif() +endif() + +if(_roff) + find_program(A2X NAMES a2x a2x.py) + if(NOT A2X) + unset(_roff) + message(WARNING + "The `a2x' program is missing. " + "The stardis man pages cannot be generated.") + endif() +endif() + +################################################################################ +# Copy doc files +################################################################################ +set(MAN_NAMES + stardis-input.5 + stardis-output.5) + +if(_roff OR _html) + set(MAN_FILES) + foreach(_name IN LISTS MAN_NAMES) + set(_src ${STARDIS_DOC_DIR}/${_name}.txt) + set(_dst ${CMAKE_CURRENT_BINARY_DIR}/${_name}.txt) + add_custom_command( + OUTPUT ${_dst} + COMMAND ${CMAKE_COMMAND} -E copy ${_src} ${_dst} + DEPENDS ${_src} + COMMENT "Copy the asciidoc ${_src}" + VERBATIM) + list(APPEND MAN_FILES ${_dst}) + endforeach() + add_custom_target(man-copy ALL DEPENDS ${MAN_FILES}) +endif() + +list(APPEND MAN_NAMES stardis.1) + +################################################################################ +# ROFF man pages +################################################################################ +if(_roff) + set(A2X_OPTS -dmanpage -fmanpage) + set(MAN_FILES) + set(MAN5_FILES) + set(MAN1_FILES) + foreach(_name IN LISTS MAN_NAMES) + set(_man ${CMAKE_CURRENT_BINARY_DIR}/${_name}) + set(_txt ${CMAKE_CURRENT_BINARY_DIR}/${_name}.txt) + + add_custom_command( + OUTPUT ${_man} + COMMAND ${A2X} ${A2X_OPTS} ${_txt} + DEPENDS man-copy ${_txt} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Build ROFF man page ${_man}" + VERBATIM) + list(APPEND MAN_FILES ${_man}) + + string(REGEX MATCH "^.*.5$" _man5 ${_man}) + string(REGEX MATCH "^.*.1$" _man1 ${_man}) + if(_man1) + list(APPEND MAN1_FILES ${_man1}) + elseif(_man5) + list(APPEND MAN5_FILES ${_man5}) + else() + message(FATAL_ERROR "Unexpected man type") + endif() + endforeach() + add_custom_target(man-roff ALL DEPENDS ${MAN_FILES}) + + install(FILES ${MAN1_FILES} DESTINATION share/man/man1) + install(FILES ${MAN5_FILES} DESTINATION share/man/man5) +endif() + +################################################################################ +# HTML documentation +################################################################################ +if(_html) + set(ASCIIDOC_OPTS + -bxhtml11 + -dmanpage + --attribute themedir=${STARDIS_DOC_DIR} + --theme=stardis-man) + + set(MAN_FILES) + set(MAN5_FILES) + set(MAN1_FILES) + foreach(_name IN LISTS MAN_NAMES) + set(_man ${CMAKE_CURRENT_BINARY_DIR}/${_name}.html) + set(_txt ${CMAKE_CURRENT_BINARY_DIR}/${_name}.txt) + + add_custom_command( + OUTPUT ${_man} + COMMAND ${ASCIIDOC} ${ASCIIDOC_OPTS} ${_txt} + DEPENDS man-copy ${_txt} ${STARDIS_DOC_DIR}/stardis-man.css + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Build HTML man page ${_man}" + VERBATIM) + list(APPEND MAN_FILES ${_man}) + + string(REGEX MATCH "^.*.5.html$" _man5 ${_man}) + string(REGEX MATCH "^.*.1.html$" _man1 ${_man}) + if(_man1) + list(APPEND MAN1_FILES ${_man1}) + elseif(_man5) + list(APPEND MAN5_FILES ${_man5}) + else() + message(FATAL_ERROR "Unexpected man type") + endif() + endforeach() + add_custom_target(man-html ALL DEPENDS ${MAN_FILES}) + + install(FILES ${MAN1_FILES} DESTINATION share/doc/stardis/html) + install(FILES ${MAN5_FILES} DESTINATION share/doc/stardis/html) +endif() + diff --git a/doc/stardis-input.5.txt b/doc/stardis-input.5.txt @@ -0,0 +1,207 @@ +// Copyright (C) 2018-2020 |Meso|Star> +// +// This program 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. +// +// This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + +:toc: + +stardis-input(5) +================= + +NAME +---- +stardis-input - thermal system description for stardis(1) + +DESCRIPTION +----------- +*stardis-input* is the format used by the *stardis*(1) program to describe a +thermal system. It relies on a line-based ad-hoc syntax. + +A thermal system is composed of lines of text, each one describing either a +medium (solid or fluid) frontier, a boundary (limit condition or connection +between two media), or the scale of the geometry. In the medium or boundary +cases, description lines include a list of file names that constitute the +limit or boundary. The current version of *stardis* only accepts triangle +mesh geometry files in *STL* format. If a scale is specified, it defines the +scaling factor to apply to the geometry to have it expressed in meters (e.g. +1e-3 if the geometry is in mm). + +A medium limit or a boundary description can be split accross files and a +single file or description line can describe more than one frontier (more than +one connex region). The main semantic constraint on descriptions is that any +enclosure must be defined by a single description line, to ensure that any +part in the system is made from a single medium. + +Finally, description lines can be submitted to the *stardis*(1) program in +any order and can be split accross more than one file, through multiple use +of option *-M*. + +UNITS +----- +Any physical quantity involved in descriptions is expected in the +International System of Units (second, metre, kilogram, kelvin, watt, joule); +the same applies to *stardis(1)* outputs as described in *stardis-output(5)*. + +However, the geometry provided to *stardis*(1) can be described in any unit, +multiple of meters or not, as long as the scaling factor is provided. + +TINIT VS TIMPOSED +----------------- +Media's descriptions, either solids or fluids, include two possible +temperatures: initial and imposed. If imposed temperature is set (that is not +"UNKNOWN"), initial temperature must be defined at the same value. As a +consequence, one cannot define a medium with an imposed temperature that +changes at t=0. + +GRAMMAR +------- +In what follows, some lines end in *\*. This is used as a convenience to +continue a description next line. However, this trick cannot be used in +actual description files and actual description lines must be kept single-line. +Also, text appearing between quote marks has to be used verbatim in the input, +except the quote characters. Finally, text introduced by the *#* character in +descriptions, when not verbatim, is a comment and is not part of the +description. + +[verse] +_______ +<thermal-system> ::= <description-lines> + [ <scaling-factor> ] + +<description-lines> ::= <description-line> + [ <description-lines> ] + +<description-line> ::= [ <medium-frontier> ] [ <comment> ] + | [ <medium-boundary> ] [ <comment> ] + | [ <media-connection> ] [ <comment> ] + +<scaling-factor> ::= "SCALE" <scaling_factor> [ <comment> ] + +------------------------------------- + +<medium-frontier> ::= <solid-frontier> + | <fluid-frontier> + +<medium-boundary> ::= <t-bound-for-solid> + | <t-bound-for-fluid> + | <h-bound-for-solid> + | <h-bound-for-fluid> + | <f-bound-for-solid> + +<media-connection> ::= <solid-fluid-connect> + +<comment> ::= "#" Any text introduced by the # character + +<scaling_factor> ::= REAL # scaling factor to apply to the geometry ; in ]0 INF) + +<solid-frontier> ::= "SOLID" <medium-name> <lambda> <rho> <cp> <delta> \ + <initial-temp> <imposed-temperature> <volumic-power> \ + <triangle-sides> + +<fluid-frontier> ::= "FLUID" <medium-name> <rho> <cp> <initial-temp> \ + <imposed-temperature> <triangle-sides> + +<t-bound-for-solid> ::= "T_BOUNDARY_FOR_SOLID" <bound-name> <temperature> <triangles> + +<t-bound-for-fluid> ::= "T_BOUNDARY_FOR_FLUID" <bound-name> <temperature> \ + <emissivity> <specular-fraction> <hc> <triangles> + +<h-bound-for-solid> ::= "H_BOUNDARY_FOR_SOLID" <bound-name> <emissivity> \ + <specular-fraction> <hc> <outside-temperature> <triangles> + +<h-bound-for-fluid> ::= "H_BOUNDARY_FOR_FLUID" <bound-name> <emissivity> \ + <specular-fraction> <hc> <outside-temperature> <triangles> + +<f-bound-for-solid> ::= "F_BOUNDARY_FOR_SOLID" <bound-name> <flux> <triangles> + +<solid-fluid-connect> ::= "SOLID_FLUID_CONNECTION" <bound-name> <emissivity> \ + <specular-fraction> <hc> <triangles> + +------------------------------------- + +<medium-name> ::= STRING # no space allowed + +<lambda> ::= REAL # conductivity in W/(m.K); in ]0, INF) + +<rho> ::= REAL # volumic mass,in kg/m3; in ]0, INF) + +<cp> ::= REAL # capacity, in J/(kg.K) or kg.m2/(s2.K); in ]0, INF) + +<delta> ::= "AUTO" # delta is automatically set to V/6A (V and A being + # respectively the solid volume and its boundary area) + | REAL # delta*scaling_factor in m; in [0, INF) + +<initial-temp> ::= REAL # in K; in [0, INF) + +<imposed-temperature> ::= "UNKNOWN" # temperature has to be solved + | REAL # in K; in [0, INF) + +<outside-temperature> ::= REAL # in K; in [0, INF) + +<volumic-power> ::= REAL # in W/m3; in (-INF , INF) + +<triangle-sides> ::= <side-specifier> <file-name> [ <triangle-sides> ] + +<bound-name> ::= STRING # no space allowed + +<emissivity> ::= REAL # in [0, 1] + +<specular-fraction> ::= REAL # in [0, 1] + +<hc> ::= REAL # in W/(m2.K); in [0, INF) + +<flux> ::= REAL # in W/m2; in (-INF , INF) + +<triangles> ::= <file-name> [ <triangles> ] + +------------------------------------- + +<side-specifier> ::= "FRONT" | "BACK" | "BOTH" + +<file-name> ::= STRING # no space allowed + +______________ + +TRIANGLE SIDES +-------------- +Side descriptions in side specifiers rely on the following convention: we +first consider the direct triangle's normal (right-hand rule), then we define +the BACK side of a triangle to be the side this normal comes out from. That +means that a closed set of triangles with normals pointing outside should be +used with the FRONT side specifier to describe inside medium. + +NAMES +----- +Names, either file names, medium names or boundary names, are a sequence of +one or ore ASCII characters, including numbers and special characters like +*.* *_* *-* as one may consider using in standard file names, *without any +spacing* either escaped or not. Names are case-sensitive anf two different +description lines, either in the same description file or from different +description files, cannot use the same name. + +EXAMPLES +-------- +Define a solid named Cube with a h boundary. The cube geometry is read from +the file cube.stl and the solid medium properties are lambda=0.1, rho=25, cp=2. +The numerical parameter delta, that is used for solid conductive walks, is +0.05. The initial temperature of the cube is 0°K and its volumic power is 0. +The boundary properties are emisivity=0, specular-fraction=0, h=10 and +external-temperature = 100°K. +....... +SOLID Cube 0.1 25 2 0.05 0 0 FRONT cube.stl +H_BOUNDARY_FOR_SOLID HdT 0 0 10 100 cube.stl +....... + +SEE ALSO +-------- +*stardis*(1) diff --git a/doc/stardis-man.css b/doc/stardis-man.css @@ -0,0 +1,96 @@ +/* Copyright (C) 2016-2018 CNRS + * + * This is free style sheet: 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. + * + * This CSS 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 this program. If not, see http://www.gnu.org/licenses/. */ + +body.manpage { + font-family:"Liberation Sans",sans-serif; + font-size:10pt; + text-align: justify; + max-width: 55em; + margin: 1em; + background: #ffffff +} + +body.manpage .monospaced, .literalblock { + margin: 2em; + color: #636261 +} + +body.manpage em { + color: #660000 +} + +body.manpage div.verseblock > pre.content { + font-family: "Liberation Mono",monospace; +} + +body.manpage h1 { + padding-bottom: 0.5em; +} +body.manpage h2 { + border-style: none; +} +body.manpage div.sectionbody { + margin-left: 3em; +} + +body.manpage code { + font-family: "Liberation Mono",monospace; +} + +body.manpage #footer { display: none; } + +body.manpage div#toctitle { display: none; } + +body.manpage div#toc { + display: block; + position:fixed; + top:0; + left:60em; + height:100%; + width: 100%; + padding:3em 0 0 0; + border-left:1px solid #dbdbdb; + background: #eeeeee +} + +body.manpage a { + font-weight: bold; + color: #225588; +} + +body.manpage div#toc a, div#toc a:link, div#toc a:visited { + margin:0; + padding-left: 2em; + color:#999999; + text-decoration:none; + font-weight: normal; +} + +body.manpage div.toclevel1 { + line-height: 1.5em; +} + +body.manpage div.toclevel2 { + margin-left: 2em; +} + +body.manpage div#toc a:hover { + color:#666666; +} + +@media print { + body.manpage div#toc { display: none; } +} + diff --git a/doc/stardis-output.5.txt b/doc/stardis-output.5.txt @@ -0,0 +1,941 @@ +// Copyright (C) 2018-2020 |Meso|Star> +// +// This program 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. +// +// This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + +:toc: + += stardis-output(5) + +== NAME + +stardis-output - output format of stardis(1) results + +== DESCRIPTION + +*stardis-output* describes the output format of the *stardis*(1) program. +Any *stardis*(1) result is written to _standard output_, even though some +additional information can be written in files. + +The type of the data that are generated depends on the options used when +*stardis*(1) is invoked. When invoked with one of the basic computation options +(*-p*, *-P*, *-m*, *-s* or *-F*), *stardis*(1) outputs a single Monte-Carlo +result. On the opposite, *stardis*(1) ouputs compound results when invoked with +option *-S* or *-R*. Additionally, options *-g* and *-G* make *stardis*(1) +compute and output a Green function and possibly information on heat paths' +ends. Most of the complex data output is in VTK [1] format that can be +displayed and manipulated by the open-source software Paraview [2]. + +Finaly, some special options (*-v*, *-h* or *-d*) that does not involve any +computation produce output including information on the *stardis*(1) software +(their ouputs will not be described thereafter) or the provided thermal system. + +== UNITS + +As with the values in *stardis-input*(5), any physical quantity in output is in +the International System of Units (second, metre, kilogram, kelvin) except the +coordinates that are in same system as the geometry. + +== OPTIONS + +Every top-level item of the output listed below is produced by one or more +specific options of the *stardis*(1) program. The following list gives the +correspondance: + +*-p* or *-P*: produce <probe-temp> + +*-m*: produces <medium-temp> + +*-s*: produces <mean-temp> + +*-F*: produces <mean-flux> + +*-p*, *-P*, *-m* or *-s* with *-g* or *-G*: produce <ascii-green> or +<binary-green> and possibly <heat-paths-ends> respectively. + +*-D*: produces <heat-path> files in addition to the simulation output + +*-R*: produces <infrared-image> + +*-d*: produces <geometry-dump> + +== GRAMMAR + +In what follows, some lines end in *\*. This is used as a convenience to +continue a description next line. However, this trick is not part of the +actual output, that continues on a single line. On the other hand, multiple +lines not using the *\* convenience in multi-lines descriptions are truly +different lines of output. Also, text appearing between quote marks is a +verbatim part of the output, except the quote characters, *#something* +denotes a count (the number of something), and the math symbols '*\+*' +and '***', when not verbatim, are used with the usual meaning (like in +#something+1). Finally, text introduced by the *#* character in a description, +when neither verbatim nor count, is a comment and is not part of the output. + +== OUTPUT + +[verse] +_______ + +<output> ::= <single-MC-result> + | <binary-green> + | <heat-paths-ends> + | <ascii-green> + | <geometry-dump> + | <infrared-image> +_______ + +== SINGLE MONTE CARLO + +When *stardis*(1) is used to produce a single Monte-Carlo result, either +temperature or flux, this result is output first to _standard output_, possibly +followed by some of the heat paths involved in the computation if option *-D* +was used too. + +Two different formats are possible: a compact, numbers only format (the default) +or an extended format that mixes numbers and their descriptions (if option *-e* +is used). + +[verse] +_______ +<single-MC-result> ::= <single-MC-result> + | <ext-single-MC-result> + +<single-MC-result> ::= <probe-temp> + | <medium-temp> + | <mean-temp> + | <mean-flux> + +<ext-single-MC-result> ::= <ext-probe-temp> + | <ext-medium-temp> + | <ext-mean-temp> + | <ext-mean-flux> + +------------------------------------- + +<probe-temp> ::= <MC-estimate> <failures-report> + +<mean-temp> ::= <MC-estimate> <failures-report> + +<mean-flux> ::= <MC-estimate> <MC-estimate> <MC-estimate> <MC-estimate> \ + <MC-estimate> <failures-report> + # MC estimates order is: temperature, convective flux, + # radiative flux, imposed flux, total flux + +<medium-temp> ::= <MC-estimate> <failures-report> + +<MC-estimate> ::= <expected-value> <standard-deviation> + +<failures-report> ::= <error-count> <success-count> + +<ext-probe-temp> ::= "Temperature at" <probe-position> <time> <MC-estimate> + <failures-report> + +<ext-mean-temp> ::= "Temperature at boundary" <file-name> <time> <MC-estimate> + <failures-report> + +<ext-mean-flux> ::= "Temperature at boundary" <file-name> <time> <MC-estimate> + "Convective flux at boundary " <file-name> <time> \ + <MC-estimate> + "Radiative flux at boundary" <file-name> <time> <MC-estimate> + "Imposed flux at boundary" <file-name> <time> <MC-estimate> + "Total flux at boundary" <file-name> <time> <MC-estimate> + <failures-report> + +<ext-medium-temp> ::= "Temperature in medium" <medium-name> <time> <MC-estimate> + <failures-report> + +<ext-probe-position> ::= "[" <x-value> "," <y-value> "," <z-value> "]" + +<ext-time> ::= "at t=" <time-value> "=" + +<ext-MC-estimate> ::= <expected-value> "+/-" <standard-deviation> + +<ext-failures-report> ::= "#failures:" <error-count> "/" <success-count> + +<ext-file-name> ::= STRING # as provided in input data + +<ext-medium-name> ::= STRING # as provided in input data + +------------------------------------- + +<x-value> ::= REAL + +<y-value> ::= REAL + +<z-value> ::= REAL + +<time-value> ::= REAL # in [0, INF) + +<error-count> ::= INTEGER # in [0, #samples] + +<success-count> ::= INTEGER # in [0, #samples] + +<expected-value> ::= REAL # depending on value semantics, range can be restricted + +<standard-deviation> ::= REAL # in [0, INF) + +_______ + +== GREEN + +The Green function is generated, either in binary or ascii format, when a +green-compatible *stardis*(1) simulation option is used in conjuction with +option *-G* for a binary output, or option *-g* for an ascii output. For +every successful heat path sampled carrying out the simulation, the solver +records all the elements of the path history relevant to link the various +imposed temperature, fluxe and volumic power values to the simulation result. +Note that to be able to explore different values of volumic power when +applying the Green function, it must have been generated with these values +being non-zero. On the other hand, any temperature or flux value in boundary +descriptions can be modified when applying the Green function, as well as the +ambient temperature. + +The output in green mode is made of tables containing the different media and +boundaries and their imposed temperature, flux and volumic power values, +followed by the heat paths' history. Also, option *-G* make it possible to +output heat paths' end information on an ascii, csv formated file. + +The Monte-Carlo estimate and standard deviation for a given set of settings can +be computed as the mean and standard deviation of the samples of the *Green +function* computed using these settings. Each sample can be computed as +follows: + +* Get the temperature of the ending boundary, medium or ambient; +* Add the temperature gain of each power term; +* Add the temperature gain of each flux term. + +=== BINARY GREEN + +Thereafter is the format of binary Green outputs. This output is produced by +fwrite calls and does not take care of endianness. Comments include the C type +of the written data. + +[verse] +_______ +<binary-green> ::= "BINGREEN" # char[8] + #descriptions # unsigned + #solids # unsigned + #fluids # unsigned + #t-boundaries # unsigned + #h-boundaries # unsigned + #flux-boundaries # unsigned + #solid-fluid-connections # unsigned + names-pool-size # size of concatenated description names: unsigned + #ok-samples # size_t + #failed-samples # size_t + <descriptions> + <concatenated-names> # char[names-pool-size] + ambient-temperature # double + reference-temperature # double + time-range # double[2] + <samples> + +<descriptions> ::= description # struct description + <descriptions> # #descriptions descriptions + +<samples> ::= <sample> + <samples> # #ok-samples samples + +--------------------- + +<sample> ::= <sample-header> # struct path_header + <ids> # unsigned[header.pcount + header.fcount] + <weights> # double[header.pcount + header.fcount] +_______ + +[literal] + /* The content of stuct str name members in descriptions is meaningless. + * The name of the ith description is the ith nul-terminated string in + * <concatenated-names>. + * As a convenience, one could use char* baz to store a pointer to the + * description's name into <concatenated-names>. */ + struct str { + void* foo; + size_t bar; + char* baz; + char qux[16]; + }; + + struct mat_fluid { + struct str name; + double rho; + double cp; + double tinit; + double imposed_temperature; + double t0; + int is_outside; + int is_green; + unsigned desc_id; + unsigned fluid_id; + }; + + struct mat_solid { + struct str name; + double lambda; + double rho; + double cp; + double delta; + double tinit; + double imposed_temperature; + double vpower; + double t0; + int is_outside; + int is_green; + unsigned desc_id; + unsigned solid_id; + }; + + struct t_boundary { + struct str name; + double emissivity; + double specular_fraction; + double hc; + double imposed_temperature; + unsigned mat_id; + }; + + struct h_boundary { + struct str name; + double emissivity; + double specular_fraction; + double hc; + double imposed_temperature; + unsigned mat_id; + }; + + struct solid_fluid_connect { + struct str name; + double emissivity; + double specular_fraction; + double hc; + unsigned connection_id; + }; + + enum description_type { + DESC_MAT_SOLID, + DESC_MAT_FLUID, + DESC_BOUND_H_FOR_FLUID, + DESC_BOUND_H_FOR_SOLID, + DESC_BOUND_T_FOR_FLUID, + DESC_BOUND_T_FOR_SOLID, + DESC_BOUND_F_FOR_SOLID, + DESC_SOLID_FLUID_CONNECT, + DESCRIPTION_TYPE_COUNT__, + DESC_OUTSIDE + }; + + struct f_boundary { + struct str name; + double imposed_flux; + unsigned mat_id; + }; + + struct description { + enum description_type type; + union { + struct mat_fluid fluid; + struct mat_solid solid; + struct t_boundary t_boundary; + struct f_boundary f_boundary; + struct h_boundary h_boundary; + struct solid_fluid_connect sf_connect; + } d; + }; + + struct path_header { + unsigned id; + unsigned pcount, fcount; + char at_initial; + }; + +=== ASCII GREEN + +Thereafter is the format of ascii Green outputs. + +[verse] +_______ +<ascii-green> ::= "---BEGIN GREEN---" + "# time range" + <time-range> + "# #solids #fluids #t_boundaries #h_boundaries #f_boundaries \ + #ok #failures" + #solids #fluids #t_boundaries #h_boundaries #f_boundaries \ + #ok #failures + "# Solids" + "# ID Name lambda rho cp power" + <solids> + "# Fluids" + "# ID Name rho cp" + <fluids> + "# T Boundaries" + "# ID Name temperature" + <t-bounds> + "# H Boundaries" + "# ID Name emissivity specular_fraction hc T_env" + <h-bounds> + "# F Boundaries" + "# ID Name flux" + <f-bounds> + "# Radiative Temperatures" + "# ID Rad_Temp Lin_Temp" + <rad-temps> + "# Samples" + "# end #power_terms #flux_terms power_term_1 ... power_term_n \ + flux_term_1 ... flux_term_n" + "# end = end_type end_id; end_type = T | H | X | R | F | S" + "# power_term_i = power_id_i factor_i" + "# flux_term_i = flux_id_i factor_i" + <samples> + "---END GREEN---" + +<time-range> ::= <REAL> <REAL> # in [0, INF) x [first REAL, INF) + +<solids> ::= <solid> + <solids> # #solids solid descriptions + +<fluids> ::= <fluid> + <fluids> # #fluids fluid descriptions + +<t-bounds> ::= <t-bound> + <t-bounds> # #t-bounds t-bound descriptions + +<h-bounds> ::= <h-bound> + <h-bounds> # #h-bounds h-bound descriptions + +<f-bounds> ::= <f-bound> + <f-bounds> # #f-bounds f-bound descriptions + +<rad-temps> ::= <green-id> <rad-temp> <lin-temp> + +<samples> ::= <sample> + <samples> # #samples sample descriptions + +------------------------------------- + +<sample> ::= <end-spec> <power-count> <flux-count> <power-terms> <flux-terms> + +<solid> ::= <green-id> <name> <lambda> <rho> <cp> <power> <initial-temp> <imposed-temp> + +<fluid> ::= <green-id> <name> <rho> <cp> <initial-temp> <imposed-temp> + +<t-bound> ::= <green-id> <name> <temperature> + +<h-bound> ::= <green-id> <name> <emissivity> <specular_fraction> <hc> \ + <temperature> + +<f-bound> ::= <green-id> <name> <flux> + +<rad-temps> ::= <green-id> <ambient-temp> <lin-temp> + +<name> ::= STRING # no space allowed + +<lambda> ::= REAL # in ]0, INF) + +<rho> ::= REAL # in ]0, INF) + +<cp> ::= REAL # in ]0, INF) + +<power> ::= REAL # in (-INF , INF) + +<initial-temp> ::= REAL # in [0 , INF) + | "NONE" if not imposed + +<imposed-temp> ::= REAL # in [0 , INF) + | "NONE" if not imposed + +<temperature> ::= REAL # in [0, INF) + +<emissivity> ::= REAL # in [0, 1] + +<specular-fraction> ::= REAL # in [0, 1] + +<hc> ::= REAL # in [0, INF) + +<flux> ::= REAL # in (-INF, INF) + +<ambient-temp> ::= REAL # in [0, INF) + +<lin-temp> ::= REAL # in [0, INF) + +<green-id> ::= INTEGER # in [0 #green-sources[ + +------------------------------------- + +<end-spec> ::= <end-type> <green-id> +<end-type> ::= "T" # sample ends at an t-bound + | "H" # sample ends at an h-bound + # a sample cannot end at an f-bound + | "A" # sample ends with ambient temperature + | "F" # sample ends in a fluid with known temperature + | "S" # sample ends in a solid with known temperature + +<power-count> ::= INTEGER # in [0 INF) + +<flux-count> ::= INTEGER # in [0 INF) + +<power-terms> ::= <power-term> + <power-terms> # <power-count> power terms + +<flux-terms> ::= <flux-term> + <flux-terms> # <flux-count> flux terms + +------------------------------------- + +<power-term> ::= <green-id> <power-factor> + +<flux-term> ::= <green-id> <flux-factor> + +<power-factor> ::= REAL # in ]0, INF) + # the temperature gain is power-factor * Power(green-id) + +<flux-factor> ::= REAL # in ]0, INF) + # the temperature gain is flux-factor * Flux(green-id) +_______ + +== HEAT PATHS' ENDS + +When computing the Green function in binary mode, stardis can output partial +information ot the sampled heat paths. Opposed to what the *-D* option outputs, +that is the complete history of selected heat paths, here the information +output is restricted to paths' ends to allow the inclusion of all the sampled +heat paths, allowing statistical analysis. + +Note that in wath follows, the meaning of external quotes is as usual: what is +inside is verbatim, including quotes. + +[verse] +_______ +<heat-paths-ends> ::= ""End", "End ID", "X", "Y", "Z", "Elapsed time"" + <heat-path-end-list> + +<heat-path-end-list> ::= <end-name> ", " <endid> ", " <x> ", " <y> ", " \ + <z> ", " <elapsed-time> + <heat-path-end-list> # #samples heat path ends + +------------------------------------- + +<end-name> ::= STRING # the name of the boundary at the end of the + # heat path, or AMBIANT for radiative ending + +<end-id> ::= INTEGER # in [0 #boundaries] + # order is the order in the description file, + # AMBIANT's id being #boundaries + +<x> ::= REAL + +<y> ::= REAL + +<z> ::= REAL + +<elapsed-time> ::= REAL # in [0, INF) +_______ + +== GEOMETRY DUMP + +A *geometry-file* is generated when *stardis*(1) is invoked with option *-d*. +In this mode, *stardis*(1) outputs the system geometry, as submitted in +*stardis-input*(5) description, to _standard output_ in VTK [1] format. +The output geometry is not the concatenation of the various geometry files +used in *stardis-input*(5) description. It is the result of a deduplication +process that removes duplicate and degenerated triangles from the submited +geometry. + +Additionaly, as permitted by the VTK [1] format, the output geometry is +decorated with many different properties provided to help users understand +the description processing, including possible errors. + +If errors are detected, some optional error-related data fields are included +in the geometry file. Some errors report a by-triangle error status, other +errors report a by-enclosure error status. + +Also, holes in the geometry, if any, are reported in geometry dumps. A hole is +defined by its frontier that is a collection of triangles surrounding the hole. +Such triangles are detected as having their 2 sides in the same enclosure, but +with a different medium on each side. + +Media information is provided in two different flavours. First the medium on +front and back sides of triangles can be found through the Front_medium and +Back_medium fields. These fields use the special value 4294967295 (INT_MAX) for +sides with no defined medium, as one can expect on boundary triangles. On the +other hand, medium information provided by the Enclosures_internal_media +field displays the id of the medium created to hold boundary information for +boundary triangles. In either case, media numbering information can be found +in log messages if option -V 3 is used in conjunction with the -d dump option. + +[verse] +_______ +<geometry-file> ::= "# vtk DataFile Version 2.0" + "Dump of star-geometry-3d geometry" + "ASCII" + "DATASET POLYDATA" + <vertices> + <triangles> + "CELL_DATA" #triangles + <front-media> + <back-media> + <interfaces> + <unique-ids> + <user-ids> + [ <merge-conflicts> ] # if some merge conflict occured + [ <property-conflicts> ] # if some property conflict occured + <file-ids> + <boundaries> + [ <compute-region> ] # if defined + <encl-or-overlaps> + +<vertices> ::= "POINTS" #vertices "double" + <vertex-list> + +<triangles> ::= "POLYGONS" #triangles #triangles*4 + <triangle-list> + +<front-media> ::= "SCALARS Front_medium unsigned_int 1" + "LOOKUP_TABLE default" + <front-medium-ids> + +<back-media> ::= "SCALARS Back_medium unsigned_int 1" + "LOOKUP_TABLE default" + <back-medium-ids> + +<interfaces> ::= "SCALARS Interface unsigned_int 1" + "LOOKUP_TABLE default" + <interface-ids> + +<unique-ids> ::= "SCALARS Unique_ID unsigned_int 1" + "LOOKUP_TABLE default" + <trg-unique-ids> + +<user-ids> ::= "SCALARS User_ID unsigned_int 1" + "LOOKUP_TABLE default" + <trg-user-ids> + +<merge-conflicts> ::= "SCALARS Merge_conflict int 1" + "LOOKUP_TABLE default" + <trg-merge-conflicts> + +<property-conflicts> ::= "SCALARS Property_conflict int 1" + "LOOKUP_TABLE default" + <trg-prop-conflicts> + +<file-ids> ::= "SCALARS Created_at_sg3d_geometry_add unsigned_int 1" + "LOOKUP_TABLE default" + <file-ranks> + +<boundaries> ::= "SCALARS Boundaries unsigned_int 1" + "LOOKUP_TABLE default" + <boundary-ids> + +<compute-region> ::= "SCALARS Compute_region unsigned_int 1" + "LOOKUP_TABLE default" + <region-membership> + +<encl-or-overlaps> ::= <encl-information> # if enclosure extraction was possible + | <overlaps> # if overlapping triangles where detected + +----------------- + +<vertex-list> ::= "3" <vertice_id> <vertice_id> <vertice_id> + <vertex-list> # #vertices vertices + +<triangle-list> ::= <real3> + <triangle-list> # #triangles triangles + +<front-medium-ids> ::= <medium-id> | <undef-medium> + <front-medium-ids> # #triangles ids + +<back-medium-ids> ::= <medium-id> | <undef-medium> + <back-medium-ids> # #triangles ids + +<interface-ids> ::= INTEGER # in [0 #interface[ + <interface-ids> # #triangles ids + +<trg-unique-ids> ::= INTEGER # in [0 #triangles[ + <trg-unique-ids> # #triangles ids + +<trg-user-ids> ::= INTEGER # in [0 #submitted triangles[ + <trg-user-ids> # #triangles ids + +<trg-merge-conflicts> ::= "0" # triangle without any merge conflict + | "1" # triangle with a merge conflict + <trg-merge-conflicts> # #triangles statuses + +<trg-prop-conflicts> ::= "0" # triangle with no property conflict + | "1" # H_BOUNDARY_FOR_FLUID between 2 defined media + | "2" # H_BOUNDARY_FOR_FLUID between 2 undefined media + | "3" # H_BOUNDARY_FOR_FLUID enclosing a solid + | "4" # H_BOUNDARY_FOR_SOLID between 2 defined media + | "5" # H_BOUNDARY_FOR_SOLID between 2 undefined media + | "6" # H_BOUNDARY_FOR_SOLID enclosing a fluid + | "7" # T_BOUNDARY_FOR_FLUID between 2 defined media + | "8" # T_BOUNDARY_FOR_FLUID between 2 undefined media + | "9" # T_BOUNDARY_FOR_FLUID enclosing a solid + | "10" # T_BOUNDARY_FOR_SOLID between 2 defined media + | "11" # T_BOUNDARY_FOR_SOLID between 2 undefined media + | "12" # T_BOUNDARY_FOR_SOLID enclosing a fluid + | "13" # F_BOUNDARY_FOR_FLUID between 2 defined media + | "14" # F_BOUNDARY_FOR_FLUID between 2 undefined media + | "15" # F_BOUNDARY_FOR_SOLID enclosing a fluid + | "16" # SOLID_FLUID_CONNECTION between 2 solids + | "17" # SOLID_FLUID_CONNECTION between 2 fluids + | "18" # SOLID_FLUID_CONNECTION used as boundary + | "19" # SOLID_FLUID_CONNECTION between 2 undefined media + | "20" # no connexion between 2 fluids + | "21" # no connexion between a solid and a fluid + | "22" # no boundary around a fluid + | "23" # no boundary around a solid + | "24" # invalid part of a compute surface + <trg-prop-conflicts> # #triangles statuses + +<real3> ::= REAL REAL REAL + +<vertice-id> ::= INTEGER # in [0 #vertices[ + +<file-ranks> ::= INTEGER # in [0 #submitted files[ + <file-ranks> # #triangles ranks + +<boundary-ids> ::= INTEGER # in [0 #submitted descriptions[ + <boundary-ids> # #triangles ids + +<region-membership> ::= <reg-not-member> # triangle not part of the compute region + | <reg-member> # triangle is part of the compute region + <region-membership> # #triangles membership status + +<encl-information> ::= [ <holes> ] # if there are holes + <enclosures> + +<overlaps> ::= "SCALARS Overlapping_triangles unsigned_int 1" + "LOOKUP_TABLE default" + <overlapping-status> + +<holes> ::= "SCALARS Hole_frontiers unsigned_int 1" + "LOOKUP_TABLE default" + <hole-memberships> + +<enclosures> ::= "FIELD FieldData 2" + "Enclosures" #enclosures #triangles "unsigned_char" + <encl-memberships> + "Enclosures_internal_media" #enclosures #triangles "unsigned_int" + <encl-media> + +----------------- + +<hole-memberships> ::= "0" # triangle not surrounding a hole + | "1" # triangle surrounding a hole + <hole-memberships> # #triangles hole memberships + +<encl-memberships> ::= <enclosure-status 0> ... <enclosure-status #enclosures-1> + <encl-memberships> # #triangles enclosure memberships + +<encl-media> ::= <enclosure 0 medium> ... <enclosure #enclosures-1 medium> + <encl-media> # #triangles enclosure media + +<medium-id> ::= INTEGER # in [0, #medium[ + +<undef-medium> ::= "4294967295" + +<reg-not-member> ::= "0" + +<reg-member> ::= "1" # the FRONT side is member of the region + | "2" # the BACK side is member of the region + | "3" # both sides are member of the region + | "4294967295" # the triangle is an invalid part of the region + +<overlapping-status> ::= "0" # triangle not overlapping another triangle + | "1" # triangle overlapping another triangle + <overlapping-status> # #triangles overlapping status + +<enclosure-status i> ::= <encl-not-member> # the triangle is not part of the ith enclosure + | <encl-member> # the triangle is part of the ith enclosure + +<enclosure i medium> ::= <medium-id> # the triangle is part of the ith enclosure and + # has this medium in the involved side(s) + | "4294967293" # the triangle is part of the ith enclosure and + # has no defined medium in the involved side(s) + | "4294967294" # the 2 sides of the triangle are part of the ith + # enclosure, but have 2 different media + | "4294967295" # the triangle is not part of the ith enclosure + +----------------- + +<encl-not-member> ::= "0" + +<encl-member> ::= "1" # valid enclosure + | "3" # invalid enclosure: more than 1 medium + | "5" # invalid enclosure: some triangles with no defined medium + | "7" # invalid enclosure: more than 1 "medium", including undefined +_______ + +== INFRARED IMAGE + +When invoked with option *-R*, *stardis*(1) generates an infrared image of +the system and write it to _standard output_. Depending on the *fmt* +sub-option, this file can be either in VTK [1] format on in *htrdr-image*(5) +format. + +=== HTRDR-IMAGE INFRARED IMAGE +If the output image is in htrdr-image format, it comply with the *lw* section +of the format, with only the temperature fields being informed. The resulting +format is as follows: + +[verse] +_______ +<htrdr-image> ::= <definition> + <pixel> + [ <pixel> ... ] + +<definition> ::= <width> <height> +<width> ::= INTEGER +<height> ::= INTEGER + +<pixel> ::= <temperature> 0 0 0 0 <time> + +<temperature> ::= <estimate> +<time> ::= <estimate> + +<estimate> ::= <expected-value> <standard-error> +<expected-value> ::= REAL +<standard-error> ::= REAL +_______ + +These files can be post-processed using the *htpp*(1) tool, that is part of +the high-tune project. + +=== VTK INFRARED IMAGE +If the output image is in VTK format, it is on an XY plane with coordinates in +the [0 pixel_count[ range. By convention, the origine (0,0) pixel is at the +top-left corner of the image. + +The result not only includes the computed temperature image, but also includes +a per-pixel computation time image as well as a per-pixel path error count +image and per-pixel standard deviation images for both temperature and +computation time. + +[verse] +_______ +<infrared-image> ::= "# vtk DataFile Version 2.0" + "Infrared Image" + "ASCII" + "DATASET STRUCTURED_POINTS" + "DIMENSIONS" <image-width> <image-height> "1" + "ORIGIN 0 0 0" + "SPACING 1 1 1" + "POINT_DATA" <image-width>*<image-height> + "SCALARS temperature_estimate float 1" + "LOOKUP_TABLE default" + <temperatures> + "SCALARS temperature_std_dev float 1" + "LOOKUP_TABLE default" + <temp_std_devs> + "SCALARS computation_time float 1" + "LOOKUP_TABLE default" + <computation_times> + "SCALARS computation_time_std_dev float 1" + "LOOKUP_TABLE default" + <com_time_std_devs> + "SCALARS failures_count unsigned_long_long 1" + "LOOKUP_TABLE default" + <failures_counts> + +<temperatures> ::= REAL # in [0, INF) + <temperatures> # <image-width>*<image-height> temperatures + +<temp_std_devs> ::= REAL # in [0, INF) + <temperature_std_devs> # <image-width>*<image-height> std_devs + +<computation_times> ::= REAL # in [0, INF) + <computation_times> # <image-width>*<image-height> times + +<comp_time_std_devs> ::= REAL # in [0, INF) + <comp_time_std_devs> # <image-width>*<image-height> std_devs + +<failures_counts> ::= INTEGER # in [0, SAMPLES_COUNT] + <failures_counts> # <image-width>*<image-height> failures_counts +_______ + +== DUMP HEAT PATHS + +When the *stardis*(1) option *-D* is used in conjunction with an option that +computes a result, some of the heat paths (successful paths, erroneous paths, +or both) sampled during the simulation are written to files. Each path is +written in VTK [1] format, one VTK file per path. The path description can +include vertices' time if it makes sense, that is if the computation time is +not INF. + +[verse] +_______ + +<heat-path> ::= "# vtk DataFile Version 2.0" + "Heat path" + "ASCII" + "DATASET POLYDATA" + "POINTS" #vertices "double" + <path-vertices> + "LINES 1" #vertices+1 + <heat-path> + "POINT_DATA" #vertices + "SCALARS Vertex_Type unsigned_char 1" + "LOOKUP_TABLE default" + <vertices-types> + "CELL_DATA 1" + "SCALAR Path_type unsigned_char 1" + "LOOKUP_TABLE default" + <path-type> + "SCALARS Weight double 1" + "LOOKUP_TABLE default" + <weigths> + [ <vertices-time> ] # if not steady + +<path-vertices> ::= <real3> + <path-vertices> # #vertices vertices + +<heat-path> ::= #vertices "0" "1" ... #vertices-1 + +<vertices-types> ::= <vertice-type> + <vertices-types> # #vertices types + +<weigths> ::= REAL + <weigths> # #vertices weigths + +<vertices-time> ::= "SCALARS Time double 1" + "LOOKUP_TABLE default" + <durations> + +----------------- + +<real3> ::= REAL REAL REAL + +<durations> ::= REAL # in [0, INF) + <durations> # #vertices durations + +<vertice-type> ::= "0" # CONDUCTION + | "1" # CONVECTION + | "2" # RADIATIVE + +<path-type> ::= "0" # SUCCESS + | "1" # FAILURE + +_______ + +== NOTES + +1. VTK file format - + <http://www.vtk.org/wp-content/uploads/2015/04/file-formats.pdf> + +2. Paraview softawre - + <https://www.paraview.org/> + +== SEE ALSO + +*stardis*(1), +*stardis-input*(5) +\ No newline at end of file diff --git a/doc/stardis.1.txt.in b/doc/stardis.1.txt.in @@ -0,0 +1,320 @@ +// Copyright (C) 2018-2020 |Meso|Star> +// +// This program 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. +// +// This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + +:toc: + +stardis(1) +=========== + +NAME +---- +stardis - statistical solving of coupled thermal systems + +SYNOPSIS +-------- +[verse] +*stardis* [_option_] +*stardis* *-M* <__file__> [_option_] + +DESCRIPTION +----------- +*stardis* solves coupled thermal systems under the linear assumption. Here +coupled refers to conductive, convective and radiative transfers, and linear +means that each phenomena is represented using a model that is linear +with temperature. *stardis* can deal with complex geometries as well as +high-frequency external solicitations over a very long period of time, +relative to the characteristic time of the system. The provided system +description should comply with the *stardis-input*(5) format. + +*stardis* can compute a thermal observable, like temperature or flux, at a +probe point and date or the mean value of an observable over a given surface, +volume, or time range. When a time range *t1, t2* is provided, the computed +value is the mean value over the time range. To compute the value at a given +time, simply provide a single value *t*. In addition, *stardis* gives access to +the evaluation of the propagator (a.k.a the *Green function*). + +The propagator is of great value for thermicist engineers as it gives some +crucial information to analyse heat transfers in the system. It helps engineers +answer questions like _"Where from does the heat come at this location?"_. +Propagators seamlessly agregate all the provided geometrical and physical +information on the system in an unbiased and very-fast statistical model. + +*stardis*(1) also provides two additional functionalities: converting the +*stardis-input*(5) geometry into a VTK file and rendering an infrared image +of the submitted system. + +Stardis' algorithms are based on state-of-the-art Monte-Carlo method applied +to radiative transfer physics (Delatorre [1]) combined with conduction's +statistical formulation (Kac [2] and Muller [3]). Thanks to recent advances in +computer graphics technology which has already been a game changer in the +cinema industry (FX and animated movies), this theoretical framework can now +be practically used on the most geometrically complex systems. While this +capability is part of the StarEngine Star3D library, it is internally powered +by Intel® Rendering Framework: Embree. + +Everytime the linear assumption is relevant, this theoretical framework allows +to encompass all the heat transfer mecanisms (conductive-convective-radiative) +in an unified statistical model. Such systems can be solved by a Monte-Carlo +approach just by sampling heat paths. This can be seen as an extension of +Monte-Carlo algorithms that solve radiative transfer by sampling optical paths. +A main property of this approach is that the resulting algorithms does not rely +on a volume mesh of the system. + +[1] Delatorre et al., Monte Carlo advances and concentrated solar applications, +Solar Energy, 2014 + +[2] Kac, On Distributions of Certain Wiener Functionals. The Annals of +Mathematical Statistics, 1949. + +[3] Muller, Some continuous Monte-Carlo Methods for the Dirichlet Problem, +Transactions of the American Mathematical Society, 1956. + +MANDATORY OPTIONS +----------------- +*-M* _file_:: + Read a text file containing a possibly partial description of the system. + Can include both media enclosures and boundary conditions, in any order. + Can be used more than once if the description is split across different + files. + +EXCLUSIVE OPTIONS +----------------- +*-p* _x,y,z[,time-range]_:: + Compute the temperature at the given probe at a given time. By default the + compute time range is @STARDIS_ARGS_DEFAULT_COMPUTE_TIME@. The probe must + be in a medium. The probe coordinates must be in the same system as the + geometry. + +*-P* _x,y,z[,time-range]_:: + Compute the temperature at the given probe on an interface at a given time. + By default the compute time range is @STARDIS_ARGS_DEFAULT_COMPUTE_TIME@. The + probe is supposed to be on an interface and is moved to the closest point of + the closest interface before the computation start. The probe coordinates + must be in the same system as the geometry. + +*-m* _medium_name[,time-range]_:: + Compute the mean temperature in a given medium at a given time. The medium + name must be part of the system description. By default the compute time + range is @STARDIS_ARGS_DEFAULT_COMPUTE_TIME@. The medium does not need to be + connex. + +*-s* _file[,time-range]_:: + Compute the mean temperature on a given 2D region at a given time, the region + being defined as the front sides of the triangles in the provided *STL* file. + By default the compute time range is @STARDIS_ARGS_DEFAULT_COMPUTE_TIME@. + These triangles are not added to the geometry, but must be part of it. The + region does not need to be connex. + +*-S* _file[,time-range]_:: + Compute the by-triangle mean temperature on a given 2D region at a given + time, the region being defined as the front sides of the triangles in the + provided *VTK* file. These triangles are not added to the geometry, but must + be part of it. By default the compute time range is + @STARDIS_ARGS_DEFAULT_COMPUTE_TIME@. The region does not need to be connex. + +*-F* _file[,time-range]_:: + Compute the mean flux on a given 2D region at a given time, the region + being defined as the front sides of the triangles in the provided *VTK* file. + These triangles are not added to the geometry, but must be part of it. Flux + is accounted positive when going from the front side to the back side, at a + single-triangle level. By default the compute time range is + @STARDIS_ARGS_DEFAULT_COMPUTE_TIME@. The region does not need to be connex, + but it can currently only include geometry appearing in description lines + starting with H_BOUNDARY_FOR_SOLID, H_BOUNDARY_FOR_FLUID, + F_BOUNDARY_FOR_SOLID or SOLID_FLUID_CONNECTION (see *stardis-input*(5)). + +*-R* [__sub-option__:...]:: + Render an infrared image of the system through a pinhole camera and write it + to _standard output_. One can use all-default sub-options by + simply providing the colon character (*:*) alone as an argument. Please note + that the camera position must be outside the geometry or in a fluid. + Available sub-options are: + + **fmt=**_image_file_format_;; + Format of the image file in output. Can be *VTK* or *HT*. Default + _image_file_format_ is @STARDIS_ARGS_DEFAULT_RENDERING_OUTPUT_FILE_FMT@. + + **fov=**_angle_;; + Horizontal field of view of the camera in [30, 120] degrees. By default + _angle_ is @STARDIS_ARGS_DEFAULT_RENDERING_FOV@ degrees. + + **img=**_width_**x**_height_;; + Definition of the rendered image in pixels. By default the image definition + is @STARDIS_ARGS_DEFAULT_RENDERING_IMG_WIDTH@x@STARDIS_ARGS_DEFAULT_RENDERING_IMG_HEIGHT@. + + **pos=**_x_**,**_y_**,**_z_;; + Position of the camera. By default it is set to + { @STARDIS_ARGS_DEFAULT_RENDERING_POS@ } or it is automatically computed to + ensure that the whole scene is visible, whether *tgt* is set or not, + respectively. + + **spp=**_samples-count_;; + Number of samples per pixel. + By default, use @STARDIS_ARGS_DEFAULT_RENDERING_SPP@ samples per pixel. + + **t=**_time-range_;; + Rendering time range. By default _time-range_ is + @STARDIS_ARGS_DEFAULT_RENDERING_TIME@. + + **tgt=**_x_**,**_y_**,**_z_;; + Position targeted by the camera. By default, it is set to + { @STARDIS_ARGS_DEFAULT_RENDERING_TGT@ } or it is automatically computed to + ensure that the whole scene is visible, whether *pos* is set or not, + respectively. + + **up=**_x_**,**_y_**,**_z_;; + Up vector of the camera. By default, it is set to + { @STARDIS_ARGS_DEFAULT_RENDERING_UP@ }. + +OTHER OPTIONS +------------- +*-a* _ambient_:: + Set the ambient radiative temperature for the whole system, in Kelvin. By + default *ambient* is @STARDIS_ARGS_DEFAULT_AMBIENT_TEMP@. + +*-d*:: + Write the geometry to _standard output_ in VTK format along with various + properties, including possible errors. If this option is used, no + computation occurs. ++ +Using this option in conjunction with an option that +specifies a compute region (-F, -S, -s) has the effect to include the +region in the output. This option cannot be used in conjunction with other +options that write to _standard output_ (-g, -h, -R, -v). + +*-D* _type,files_name_prefix_:: + Write sampled heat paths of the given *type* to files in VTK format, one + file per path. Possible values for *type* are *error* (write paths ending + in error), *success* (write successful paths), and *all* (write all paths). + Actual file names are produced by appending *files_name_prefix* and the path + rank starting at index 00000000, and possibly followed by *_err* for failure + paths: prefix00000000.vtk, prefix00000001_err.vtk, ... ++ +This option can only be used in conjuction with options that compute a +result (-F, -m, -P, -p, -R, -S, -s) and cannot be used in conjunction with +options -g or -G. + +*-e*:: + Use extended format to output Monte-Carlo results. Can only be used in + conjunction with options that compute a single Monte-Carlo (-F, -m, -P, -p, + or -s without options -g or -G). + +*-g*:: + Compute the Green function at the specified time and write it in ASCII to + _standard output_. ++ +This option can only be used in conjunction with one these options: -p, -P, +-m, -s and cannot be used in conjunction with option -D. + +*-G* _file_name_[,__file_name__]:: + Compute the Green function at the specified time and write it to a binary + file. If a second file name is provided, information on heat paths' ends + is also written in this second file in ascii csv format. ++ +This option can only be used in conjunction with one these options: -p, -P, +-m, -s and cannot be used in conjunction with option -D. ++ +The resulting file can be further used through the *sgreen*(1) command to apply +different temperature, flux or volumic power values. + +*-h*:: + Output short help and exit. + +*-n* _samples-count_:: + Number of Monte-Carlo samples. By default *samples-count* is set to + @STARDIS_ARGS_DEFAULT_SAMPLES_COUNT@. + +*-r* _reference_:: + Set the reference temperature used for the linearization of the radiative + transfer, in Kelvin. By default *reference* + is @STARDIS_ARGS_DEFAULT_REFERENCE_TEMP@. + +*-t* _threads-count_:: + Hint on the number of threads to use. By default use as many threads as CPU + cores. + +*-v*:: + Output version information and exit. + +*-V* _level_:: + Set the verbosity level. Possible values are *0* (no message), *1* (error + messages only), *2* (error and warning messages), and *3* (error, warning + and informative messages). All the messages are written to _standard error_. + Default verbosity *level* is @STARDIS_ARGS_DEFAULT_VERBOSE_LEVEL@. + +EXAMPLES +-------- +Preprocess the system as described in *scene 5.txt* when intending to compute +the mean flux on the triangles from the file *edge.stl*, and write its geometry +in the file *scene.vtk*. Verbosity level is set to *3*: + + $ stardis -M "scene 5.txt" -F edge.stl -d -V 3 > scene.vtk + +Compute the temperature at the probe point *0, 0.5, 0* at steady state. The +system is read from the file *model.txt* and the number of samples is set to +*1000000*: + + $ stardis -M model.txt -p 0,0.5,0 -n 1000000 + +Compute the mean temperature in the medium *med05* at *t=100* s. The system is +read from the file *model.txt* and the result is output with extended format: + + $ stardis -M model.txt -m med05,100 -e + +Compute the temperature at the probe point *0, 0, 0* at *t=2500*. The system is +read from the 2 files *media.txt* and *bounds.txt* and the number of samples is +set to *1000000*: + + $ stardis -M media.txt -M bounds.txt -p 0,0,0,2500 -n 1000000 + +Compute the mean temperature at the probe point *1, 2.5, 0* over the *50, 5000* +time range. The system is read from the file *model.txt*: + + $ stardis -M model.txt -p 1,2.5,0,50,5000 + +Render the system as described in *scene.txt* with default settings: + + $ stardis -M scene.txt -R : + +Render the system as described in *scn.txt* at *t=100*, *spp=2*, +*img=800x600*, with output format *fmt=ht* and all other settings set to their +default values. The output is redirected to the *img.ht* file. If the +computation encounters erroneous heat paths, they will be dumped to VTK files +named err_path_00000000.vtk, err_path_00000001.vtk, etc. The image file is then +post-processed using *htpp*(1) with default settings to obtain a png file. + + $ stardis -M scn.txt -R t=100:spp=2:img=800x600:fmt=ht -D error,err_path_ > img.ht + $ htpp -o img.pgn -v -m default img.ht + +Compute the Green fonction that computes the temperature at the probe point +*0, 0, 0* at steady state. The system is read from the file *model.txt* and +the Green function is written to the *probe.green file* and the heat paths' +ends are written to the *probe_ends.csv* file: + + $ stardis -M model.txt -p 0,0,0 -G probe.green,probe_ends.csv + +COPYRIGHT +--------- +Copyright &copy; 2018-2020 |Meso|Star>. License GPLv3+: GNU GPL +version 3 or later <http://gnu.org/licenses/gpl.html>. This is free software. +You are free to change and redistribute it. There is NO WARRANTY, to the extent +permitted by law. + +SEE ALSO +-------- +*stardis-input*(5), +*stardis-output*(5), +*sgreen*(1), +*htpp*(1) diff --git a/pp/green2xslx.pl b/pp/green2xslx.pl @@ -1,567 +0,0 @@ -#!/usr/bin/perl -use strict; -use warnings; - -use Excel::Writer::XLSX; -use Data::Dumper; - -# -# Read and parse STDIN -# - -# Drop warnings and text until leading '---BEGIN GREEN---' -my $found_start = 0; -my $line; -while ($line = <STDIN>) { - chomp $line; - if ($line eq "---BEGIN GREEN---") { $found_start = 1; last;} -} -die 'No green found!' unless $found_start; - -# Read counters -my $solids_rk=0; -my $fluids_rk=1; -my $tbounds_rk=2; -my $hbounds_rk=3; -my $fbounds_rk=4; -my $ok_rk=5; -my $failed_rk=6; -$line = <STDIN>; -chomp $line; -die "Unexpected content found ($line)!" unless $line eq "# #solids #fluids #t_boundaries #h_boundaries #f_boundaries #ok #failures"; -$line = <STDIN>; -chomp $line; -my @counts = $line =~ /(\d+)/g; - -# Need at least 1 successful sample to proceed! -die "No successful samples in this green ($counts[$failed_rk] ) failed samples\n" unless $counts[$ok_rk]; - -my $last_id = -1; -my @seen_id_types; - -# Read Solids -my @solids; -if ($counts[$solids_rk] > 0) { - $line = <STDIN>; - chomp $line; - die "Unexpected content found ($line)!" unless ($line eq "# Solids"); - $line = <STDIN>; - chomp $line; - die "Unexpected content found ($line)!" unless ($line eq "# ID Name lambda rho cp power"); - for (my $s = 0; $s < $counts[$solids_rk] ; $s++) { - $line = <STDIN>; - chomp $line; - my @tmp = split("\t", $line); - die "Wrong number of values!" unless (scalar(@tmp) == 6); - die "Wrong ID" unless ($tmp[0] >= 0) && (! defined $seen_id_types[$tmp[0]]); - $seen_id_types[$tmp[0]] = 'S'; - my %new_elt = ( - ID=>$tmp[0], - TEMP=>-1, # Imposed temperature for solids not yet in Stardis-app - NAME=>$tmp[1], - LAMBDA=>$tmp[2], - RHO=>$tmp[3], - CP=>$tmp[4], - POWER=>$tmp[5] - ); - push @solids, \%new_elt; - } -} - -# Read Fluids -my @fluids; -if ($counts[$fluids_rk] > 0) { - $line = <STDIN>; - chomp $line; - die "Unexpected content found ($line)!" unless ($line eq "# Fluids"); - $line = <STDIN>; - chomp $line; - die "Unexpected content found ($line)!" unless ($line eq "# ID Name rho cp"); - for (my $f = 0; $f < $counts[$fluids_rk] ; $f++) { - $line = <STDIN>; - chomp $line; - my @tmp = split("\t", $line); - die "Wrong number of values!" unless (scalar(@tmp) == 4); - die "Wrong ID" unless ($tmp[0] >= 0) && (! defined $seen_id_types[$tmp[0]]); - $seen_id_types[$tmp[0]] = 'F'; - my %new_elt = ( - ID=>$tmp[0], - TEMP=>-1, # Imposed temperature for fluids not yet in Stardis-app - NAME=>$tmp[1], - RHO=>$tmp[2], - CP=>$tmp[3], - POWER=>0 # Volumic Power for fluids not yet in Stardis-app - ); - push @fluids, \%new_elt; - } -} - -# Read T Boundaries -my @t_boundaries; -if ($counts[$tbounds_rk] > 0) { - $line = <STDIN>; - chomp $line; - die "Unexpected content found ($line)!" unless ($line eq "# T Boundaries"); - $line = <STDIN>; - chomp $line; - die "Unexpected content found ($line)!" unless ($line eq "# ID Name temperature"); - for (my $b = 0; $b < $counts[$tbounds_rk] ; $b++) { - $line = <STDIN>; - chomp $line; - my @tmp = split("\t", $line); - die "Wrong number of values!" unless (scalar(@tmp) == 3); - die "Wrong ID" unless ($tmp[0] >= 0) && (! defined $seen_id_types[$tmp[0]]); - $seen_id_types[$tmp[0]] = 'T'; - my %new_elt = ( - ID=>$tmp[0], - NAME=>$tmp[1], - TEMP=>$tmp[2] - ); - push @t_boundaries, \%new_elt; - } -} - -# Read H Boundaries -my @h_boundaries; -if ($counts[$hbounds_rk] > 0) { - $line = <STDIN>; - chomp $line; - die "Unexpected content found ($line)!" unless ($line eq "# H Boundaries"); - $line = <STDIN>; - chomp $line; - die "Unexpected content found ($line)!" unless ($line eq "# ID Name emissivity specular_fraction hc hc_max T_env"); - for (my $b = 0; $b < $counts[$hbounds_rk] ; $b++) { - $line = <STDIN>; - chomp $line; - my @tmp = split("\t", $line); - die "Wrong number of values!" unless (scalar(@tmp) == 7); - die "Wrong ID" unless ($tmp[0] >= 0) && (! defined $seen_id_types[$tmp[0]]); - $seen_id_types[$tmp[0]] = 'H'; - my %new_elt = ( - ID=>$tmp[0], - NAME=>$tmp[1], - EMISSIVITY=>$tmp[2], - SPEC_FRACTION=>$tmp[3], - HC=>$tmp[4], - HC_MAX=>$tmp[5], - T_ENV=>$tmp[6] - ); - push @h_boundaries, \%new_elt; - } -} - -# Read F Boundaries -my @f_boundaries; -if ($counts[$fbounds_rk] > 0) { - $line = <STDIN>; - chomp $line; - die "Unexpected content found ($line)!" unless ($line eq "# F Boundaries"); - $line = <STDIN>; - chomp $line; - die "Unexpected content found ($line)!" unless ($line eq "# ID Name flux"); - for (my $b = 0; $b < $counts[$fbounds_rk] ; $b++) { - $line = <STDIN>; - chomp $line; - my @tmp = split("\t", $line); - die "Wrong number of values!" unless (scalar(@tmp) == 3); - die "Wrong ID" unless ($tmp[0] >= 0) && (! defined $seen_id_types[$tmp[0]]); - $seen_id_types[$tmp[0]] = 'X'; - my %new_elt = ( - ID=>$tmp[0], - NAME=>$tmp[1], - FLUX=>$tmp[2] - ); - push @f_boundaries, \%new_elt; - } -} - -# Read Radiative Temperatures -my $radiative_temp; -my $linear_temp; -my $rad_temp_id; -{ - $line = <STDIN>; - chomp $line; - die "Unexpected content found ($line)!" unless ($line eq "# Radiative Temperatures"); - $line = <STDIN>; - chomp $line; - die "Unexpected content found ($line)!" unless ($line eq "# ID Rad_Temp Lin_Temp"); - $line = <STDIN>; - chomp $line; - my @tmp = split("\t", $line); - die "Wrong number of values!" unless (scalar(@tmp) == 3); - die "Wrong ID" unless ($tmp[0] >= 0) && (! defined $seen_id_types[$tmp[0]]); - $seen_id_types[$tmp[0]] = 'R'; - die "Wrong Temperature!" unless ($tmp[1] >= 0) && ($tmp[2] >= 0); - $radiative_temp = $tmp[1]; - $linear_temp = $tmp[2]; - $rad_temp_id = $tmp[0]; -} - -# Read Samples headers -$line = <STDIN>; -chomp $line; -die "Unexpected content found ($line)!" unless ($line eq "# Samples"); -$line = <STDIN>; -chomp $line; -die "Unexpected content found ($line)!" unless ($line eq "# end #power_terms #flux_terms power_term_1 ... power_term_n flux_term_1 ... flux_term_n"); -$line = <STDIN>; -chomp $line; -die "Unexpected content found ($line)!" unless ($line eq "# end = end_type end_id; end_type = T | H | X | R | F | S"); -$line = <STDIN>; -chomp $line; -die "Unexpected content found ($line)!" unless ($line eq "# power_term_i = power_type_i power_id_i factor_i"); -$line = <STDIN>; -chomp $line; -die "Unexpected content found ($line)!" unless ($line eq "# flux_term_i = flux_id_i factor_i"); - -# read samples -my @samples; -for(my $s=0; $s < $counts[$ok_rk]; $s++) { - $line = <STDIN>; - die "Unexpected end of data ($s samples read)" unless $line; - chomp $line; - my @tmp = split("\t", $line); - - # Check read data - die "Wrong number of values!" unless (scalar(@tmp) >= 4); - my $pw_count = $tmp[2]; - my $fx_count = $tmp[3]; - die "Wrong power_terms count!" unless ($pw_count >=0); - die "Wrong flux_terms count!" unless ($fx_count >= 0); - die "Wrong number of terms!" unless (scalar(@tmp) == 4 + $pw_count * 3 + $fx_count * 2); - - my @pw_types; - my @pw_ids; - my @pw_factors; - my @fx_ids; - my @fx_factors; - for(my $n = 0; $n < $pw_count; $n++) { - my $ty = $tmp[4+3*$n] ; - my $id = $tmp[5+3*$n]; - my $fc =$tmp[6+3*$n]; - die "Wrong ID" unless ($id >= 0) && (defined $seen_id_types[$id]) && ($seen_id_types[$id] eq $ty); - die "Wrong power_term factor!" unless ($fc > 0); - push @pw_types, $ty; - push @pw_ids, $id; - push @pw_factors, $fc; - } - for(my $n = 0; $n < $fx_count; $n++) { - my $id = $tmp[4+$pw_count*3+2*$n]; - my $fc =$tmp[5+$pw_count*3+2*$n]; - die "Wrong ID" unless ($id >= 0) && (defined $seen_id_types[$id]) && ($seen_id_types[$id] eq 'X'); - die "Wrong power_term factor!" unless ($fc > 0); - push @fx_ids, $id; - push @fx_factors, $fc; - } - my %new_elt = ( - END_TYPE=>$tmp[0], - END_ID=>$tmp[1], - PW_COUNT=>$tmp[2], - FX_COUNT=>$tmp[3], - PW_TYPES=>\@pw_types, - PW_IDS=>\@pw_ids, - PW_FACTORS=>\@pw_factors, - FX_IDS=>\@fx_ids, - FX_FACTORS=>\@fx_factors - ); - push @samples, \%new_elt; -} - -# Check end of data -$line = <STDIN>; -chomp $line; -die "Unexpected content in file" unless ($line eq "---END GREEN---"); - -# -# Export data into an xlsx file -# - -binmode( STDOUT ); -my $workbook = Excel::Writer::XLSX->new( \*STDOUT ); -$workbook->set_properties( - title => 'Green calculator', - author => 'meso-star.com', - comments => 'Created with Perl, Excel::Writer::XLSX and Stardis-app Post-Process', - ); - -my $title = $workbook->add_format(); -$title->set_locked(1); -$title->set_bold(); -$title->set_top(5); -$title->set_bottom(2); -my $locked = $workbook->add_format(); -$locked->set_locked(1); -$locked->set_bg_color('#F2F2F2'); #light gray -my $unlocked = $workbook->add_format(); -$unlocked->set_locked(0); - -my $model_current_line = 0; -my @id_end_cells; -my @id_pw_cells; -my @id_fx_cells; -my @cpt_end_cells; -my @cpt_pw_cells; -my @cpt_fx_cells; -my @name_end_cells; -my @name_pw_cells; -my @name_fx_cells; - -# One sheet for all but samples -my $model = $workbook->add_worksheet('Model'); -$model->protect(); # Cannot edit cells unless unlocked -$model->set_column(0, 5, 24); # Column 0 to 5: width = 24 - -# Create table for solids -if($counts[$solids_rk] > 0) { - my @solid_colnames = ('Solid Name', 'Imposed Temperature', 'Lambda', 'Rho', 'Cp', 'Volumic Power'); - $model->write($model_current_line, 0, \@solid_colnames, $title); - $model_current_line++; - for(my $s = 0; $s < $counts[$solids_rk] ; $s++) { - my $solid = $solids[$s]; - my $temp = $solid->{TEMP}; - my $pw = $solid->{POWER}; - my $name = $solid->{NAME}; - $model->write($model_current_line, 0, $name, $locked); - $model->write_number($model_current_line, 1, $temp, ($temp >= 0) ? $unlocked : $locked); - $model->write_number($model_current_line, 2, $solid->{LAMBDA}, $locked); - $model->write_number($model_current_line, 3, $solid->{RHO}, $locked); - $model->write_number($model_current_line, 4, $solid->{CP}, $locked); - $model->write_number($model_current_line, 5, $pw, ($pw > 0) ? $unlocked : $locked); - $model_current_line++; - my $id = $solid->{ID}; - die "Inconsistency!" unless ($id >= 0) && (defined $seen_id_types[$id]) && ($seen_id_types[$id] eq 'S'); - die "Inconsistency!" unless (!defined $id_end_cells[$id]) && (!defined $cpt_end_cells[$id]) && (!defined $name_end_cells[$id]); - die "Inconsistency!" unless (!defined $id_pw_cells[$id]) && (!defined $cpt_pw_cells[$id]) && (!defined $name_pw_cells[$id]); - if ($temp >= 0) { - $id_end_cells[$id] = "B$model_current_line"; - $cpt_end_cells[$id] = 0; - $name_end_cells[$id] = "$name.Imposed_Temperature"; - } - $id_pw_cells[$id] = "F$model_current_line"; - $cpt_pw_cells[$id] = 0; - $name_pw_cells[$id] = "$name.Volumic_Power"; - } - $model_current_line++; -} - -# Create table for fluids -if($counts[$fluids_rk] > 0) { - my @fluid_colnames = ('Fluid Name', 'Imposed Temperature', 'Rho', 'Cp', 'Volumic Power'); - $model->write($model_current_line, 0, \@fluid_colnames, $title); - $model_current_line++; - for(my $f = 0; $f < $counts[$fluids_rk] ; $f++) { - my $fluid = $fluids[$f]; - my $temp = $fluid->{TEMP}; - my $pw = $fluid->{POWER}; - my $name = $fluid->{NAME}; - $model->write($model_current_line, 0, $name, $locked); - $model->write_number($model_current_line, 1, $temp, ($temp >= 0) ? $unlocked : $locked); - $model->write_number($model_current_line, 2, $fluid->{RHO}, $locked); - $model->write_number($model_current_line, 3, $fluid->{CP}, $locked); - $model->write_number($model_current_line, 4, $pw, ($pw > 0) ? $unlocked : $locked); - $model_current_line++; - my $id = $fluid->{ID}; - die "Inconsistency!" unless ($id >= 0) && (defined $seen_id_types[$id]) && ($seen_id_types[$id] eq 'F'); - die "Inconsistency!" unless (!defined $id_end_cells[$id]) && (!defined $cpt_end_cells[$id]) && (!defined $name_end_cells[$id]); - die "Inconsistency!" unless (!defined $id_pw_cells[$id]) && (!defined $cpt_pw_cells[$id]) && (!defined $name_pw_cells[$id]); - if ($temp >= 0) { - $id_end_cells[$id] = "B$model_current_line"; - $cpt_end_cells[$id] = 0; - $name_end_cells[$id] = "$name.Imposed_Temperature"; - } - $id_pw_cells[$id] = "E$model_current_line"; - $cpt_pw_cells[$id] = 0; - $name_pw_cells[$id] = "$name.Volumic_Power"; - } - $model_current_line++; -} - -# Create table for T boundaries -if($counts[$tbounds_rk] > 0) { - my @tbound_colnames = ('T Boundary Name', 'Temperature'); - $model->write($model_current_line, 0, \@tbound_colnames, $title); - $model_current_line++; - for(my $b = 0; $b < $counts[$tbounds_rk] ; $b++) { - my $tbound = $t_boundaries[$b]; - my $temp = $tbound->{TEMP}; - my $name = $tbound->{NAME}; - $model->write($model_current_line, 0, $name, $locked); - $model->write_number($model_current_line, 1, $temp, ($temp >= 0) ? $unlocked : $locked); - $model_current_line++; - my $id = $tbound->{ID}; - die "Inconsistency!" unless ($id >= 0) && (defined $seen_id_types[$id]) && ($seen_id_types[$id] eq 'T'); - die "Inconsistency!" unless (!defined $id_end_cells[$id]) && (!defined $cpt_end_cells[$id]) && (!defined $name_end_cells[$id]); - $id_end_cells[$id] = "B$model_current_line"; - $cpt_end_cells[$id] = 0; - $name_end_cells[$id] = "$name.Temperature"; - # No volumic power at boundaries! - } - $model_current_line++; -} - -# Create table for H boundaries -if($counts[$hbounds_rk] > 0) { - my @hbound_colnames = ('H Boundary Name', 'Emissivity', 'Specular Fraction', 'Hc', 'Hc Max', 'Environment Temperature'); - $model->write($model_current_line, 0, \@hbound_colnames, $title); - $model_current_line++; - for(my $b = 0; $b < $counts[$hbounds_rk] ; $b++) { - my $hbound = $h_boundaries[$b]; - my $name = $hbound->{NAME}; - $model->write($model_current_line, 0, $name, $locked); - $model->write_number($model_current_line, 1, $hbound->{EMISSIVITY}, $locked); - $model->write_number($model_current_line, 2, $hbound->{SPEC_FRACTION}, $locked); - $model->write_number($model_current_line, 3, $hbound->{HC}, $locked); - $model->write_number($model_current_line, 4, $hbound->{HC_MAX}, $locked); - $model->write_number($model_current_line, 5, $hbound->{T_ENV}, $unlocked); - $model_current_line++; - my $id = $hbound->{ID}; - die "Inconsistency!" unless ($id >= 0) && (defined $seen_id_types[$id]) && ($seen_id_types[$id] eq 'H'); - die "Inconsistency!" unless (!defined $id_end_cells[$id]) && (!defined $cpt_end_cells[$id]) && (!defined $name_end_cells[$id]); - $id_end_cells[$id] = "F$model_current_line"; - $cpt_end_cells[$id] = 0; - $name_end_cells[$id] = "$name.Environment_Temperature"; - # No volumic power at boundaries! - } - $model_current_line++; -} - -# Create table for F boundaries -if($counts[$fbounds_rk] > 0) { - my @fbound_colnames = ('F Boundary Name', 'Flux'); - $model->write($model_current_line, 0, \@fbound_colnames, $title); - $model_current_line++; - for(my $b = 0; $b < $counts[$fbounds_rk] ; $b++) { - my $fbound = $f_boundaries[$b]; - my $name = $fbound->{NAME}; - $model->write($model_current_line, 0, $name, $locked); - $model->write_number($model_current_line, 1, $fbound->{FLUX}, $unlocked); - $model_current_line++; - my $id = $fbound->{ID}; - die "Inconsistency!" unless ($id >= 0) && (defined $seen_id_types[$id]) && ($seen_id_types[$id] eq 'X'); - die "Inconsistency!" unless (!defined $id_fx_cells[$id]) && (!defined $cpt_fx_cells[$id]) && (!defined $name_fx_cells[$id]); - $id_fx_cells[$id] = "B$model_current_line"; - $cpt_fx_cells[$id] = 0; - $name_fx_cells[$id] = "$name.Flux"; - # Cannot end at F boundaries - # No volumic power at boundaries! - } - $model_current_line++; -} - -# Create table for Radiative Temperatures -{ - my @radiative_colnames = ('Radiative Temperature', 'Linearisation Temperature'); - $model->write($model_current_line, 0, \@radiative_colnames, $title); - $model_current_line++; - $model->write_number($model_current_line, 0, $radiative_temp, $unlocked); - $model->write_number($model_current_line, 1, $linear_temp, $locked); - $model_current_line++; - die "Inconsistency!" unless (!defined $id_end_cells[$rad_temp_id]) && (!defined $cpt_end_cells[$rad_temp_id]) && (!defined $name_end_cells[$rad_temp_id]); - $id_end_cells[$rad_temp_id]= "A$model_current_line"; - $cpt_end_cells[$rad_temp_id] = 0; - $name_end_cells[$rad_temp_id] = 'Radiative_Temperature'; - # No volumic power at infinity! - $model_current_line++; -} - -# One sheet for samples -my $green = $workbook->add_worksheet('Samples'); -$green->protect(); - -my $samples_current_line = 0; -if($counts[$ok_rk] > 0) { - my @samples_colnames = ('T(sample)'); - $green->write($samples_current_line, 0, \@samples_colnames, $title); - $samples_current_line++; - for(my $s = 0; $s < $counts[$ok_rk] ; $s++) { - my $sample = $samples[$s]; - my $end_type = $sample->{END_TYPE}; - my $end_id = $sample->{END_ID}; - my $pw_count = $sample->{PW_COUNT}; - my $pw_types = $sample->{PW_TYPES}; - my $pw_ids = $sample->{PW_IDS}; - my $pw_factors = $sample->{PW_FACTORS}; - my $fx_count = $sample->{FX_COUNT}; - my $fx_ids = $sample->{FX_IDS}; - my $fx_factors = $sample->{FX_FACTORS}; - - # create a cell with formula: - # end_temp + pw_term_1 + ... + pw_term_n + fx_term_1 + ... + fx_term_n - unless (($end_id >= 0) && ($end_id < scalar(@seen_id_types)) && (defined $seen_id_types[$end_id]) && ($seen_id_types[$end_id] eq $end_type)) { - print STDERR "Sample: ", Dumper($sample); - print STDERR "END ID: $end_id\n"; - print STDERR "END TYPE: $seen_id_types[$end_id] VS $end_type\n"; - print STDERR "END TYPES: @seen_id_types\n"; - die "Invalid ID!" ; - } - unless ((defined $id_end_cells[$end_id]) && (defined $cpt_end_cells[$end_id])) { - print STDERR "Sample: ", Dumper($sample); - print STDERR "END ID: $end_id\n"; - print STDERR "END END CELLS: @id_end_cells\n"; - die "Inconsistency!" - } - my $formula = "=Model!$id_end_cells[$end_id]"; - $cpt_end_cells[$end_id] += 1; - for(my $n = 0; $n < $pw_count; $n++) { - my $ty = @$pw_types[$n]; - my $id = @$pw_ids[$n]; - my $fc = @$pw_factors[$n]; - die "Invalid ID ($id)!" unless ($id >= 0) && (defined $seen_id_types[$id]) && ($seen_id_types[$id] eq $ty); - die "Inconsistency!" unless (defined $id_pw_cells[$id]) && ($fc > 0); - $formula = $formula."+Model!$id_pw_cells[$id]*$fc"; - $cpt_pw_cells[$id] += $fc; - } - for(my $n = 0; $n < $fx_count; $n++) { - my $id = @$fx_ids[$n]; - my $fc = @$fx_factors[$n]; - die "Invalid ID ($id)!" unless ($id >= 0) && (defined $seen_id_types[$id]); - die "Inconsistency!" unless (defined $id_fx_cells[$id]) && ($fc > 0); - $formula = $formula."+Model!$id_fx_cells[$id]*$fc"; - $cpt_fx_cells[$id] += $fc; - } - $green->write_formula($samples_current_line, 0, $formula, $locked); - $samples_current_line++; - } -} - -# Create the polynom for temperature -my $sep =""; -my $poly = "T = "; -for (my $n = 0; $n < scalar(@cpt_end_cells); $n++) { - if (defined $cpt_end_cells[$n] && $cpt_end_cells[$n] != 0) { - my $w = $cpt_end_cells[$n] / $counts[$ok_rk]; - $poly .= $sep . $w . " * $name_end_cells[$n]"; - $sep = " + "; - } -} -for(my $n = 0; $n < scalar(@cpt_pw_cells); $n++) { - if (defined $cpt_pw_cells[$n] && $cpt_pw_cells[$n] != 0) { - my $w = $cpt_pw_cells[$n] / $counts[$ok_rk]; - $poly .= $sep . $w . " * $name_pw_cells[$n]"; - $sep = " + "; - } -} -for(my $n = 0; $n < scalar(@cpt_fx_cells); $n++) { - if (defined $cpt_fx_cells[$n] && $cpt_fx_cells[$n] != 0) { - my $w = $cpt_fx_cells[$n] / $counts[$ok_rk]; - $poly .= $sep . $w . " * $name_fx_cells[$n]"; - $sep = " + "; - } -} - - -# The MC Estimator and STDERR table -my @result_colnames = ('Estimate', 'Sigma'); -$model->write($model_current_line, 0, \@result_colnames, $title); -$model_current_line++; -$model->write_formula($model_current_line, 0, "AVERAGE(Samples!A2:A$samples_current_line)", $locked); -# Here we use STDEVP (Standard dev for a entire population) to remains coherent with stardis-solver -# One could consider STDEV (Standard dev for a sampling of the population) more appropriate -$model->write_formula($model_current_line, 1, "STDEVP(Samples!A2:A$samples_current_line)/SQRT($samples_current_line-1)", $locked); -$model_current_line++; - -$model->write_string($model_current_line, 0, $poly); -$workbook->close(); - -print STDERR "$poly\n"; diff --git a/src/args.h b/src/args.h @@ -1,373 +0,0 @@ -/* Copyright (C) 2018 |Meso|Star> (contact@meso-star.com)*/ - -#ifndef ARGS_H -#define ARGS_H - -#include <getopt.h> -#include <stdlib.h> - -#include <rsys/rsys.h> -#include <rsys/cstr.h> -#include <sdis_version.h> - -enum stardis_mode { - UNDEF_MODE = 0, - PROBE_COMPUTE = BIT(0), - MEDIUM_COMPUTE = BIT(1), - BOUNDARY_COMPUTE = BIT(2), - GREEN_MODE = BIT(3), - IR_COMPUTE = BIT(4), - DUMP_VTK = BIT(5), - PROBE_COMPUTE_ON_INTERFACE = BIT(6), - COMPUTE_MODES = PROBE_COMPUTE | MEDIUM_COMPUTE | BOUNDARY_COMPUTE | IR_COMPUTE | DUMP_VTK | PROBE_COMPUTE_ON_INTERFACE -}; - -static char mode_option(const enum stardis_mode m) { - int found = 0; - char res = '?'; - if (m & PROBE_COMPUTE) { found++; res = 'p'; } - if (m & MEDIUM_COMPUTE) { found++; res = 'm'; } - if (m & BOUNDARY_COMPUTE) { found++; res = 'b'; } - if (m & IR_COMPUTE) { found++; res = 'R'; } - if (m & DUMP_VTK) { found++; res = 'd'; } - if (m & PROBE_COMPUTE_ON_INTERFACE) { found++; res = 'P'; } - ASSERT(found == 1); - return res; -} - -enum dump_path_type { - DUMP_NONE = 0, - DUMP_SUCCESS = BIT(0), - DUMP_ERROR = BIT(1), - DUMP_ALL = DUMP_SUCCESS | DUMP_ERROR, -}; - -struct args{ - char* medium_filename; - char* bc_filename; - char* medium_name; - char* solve_boundary_filename; - size_t N; - unsigned nthreads; - union u { /* Trick to allow static initialization with INF */ - struct pt { double p[3]; uint64_t t; } pt; - double probe[4]; - } u; - enum stardis_mode mode; - double scale_factor; - double radiative_temp[2]; - char* camera; - enum dump_path_type dump_paths; - int just_help; -}; -#ifdef NDEBUG -#define DEFAULT_NTHREADS SDIS_NTHREADS_DEFAULT -#else -#define DEFAULT_NTHREADS 1 -#endif -#define ARGS_DEFAULT__ {\ - NULL, NULL, NULL, NULL, 10000, DEFAULT_NTHREADS,\ - { { {0.0, 0.0, 0.0}, 0x7FF0000000000000 /* probe[3]=INF */}},\ - UNDEF_MODE, 1.0, {300.0, 300.0}, NULL, DUMP_NONE, 0} -static const struct args ARGS_DEFAULT = ARGS_DEFAULT__; - -static void -print_help(char* prog) -{ - printf("stardis-app built-on stardis library version %i.%i.%i\n", - Stardis_VERSION_MAJOR,Stardis_VERSION_MINOR,Stardis_VERSION_PATCH); - printf("usage: \n%s -M MEDIUM.txt -B BOUNDARY.txt\n", prog); - printf("[ -p X,Y,Z,TIME | -m MEDIUM_NAME,TIME | -d | -b SURFACE.vtk,TIME\n"); - printf(" | -R t=TIME:spp=SPP:fov=FOV:up=XUP,YUP,ZUP:pos=X,Y,Z:tgt=XT,YT,ZT:img=WxH ]\n"); - printf("[ -g] [-s SCALE_FACTOR] [-D {all | error | success}] -d\n"); - printf("[-n NUM_OF_REALIZATIONS] [-t NUM_OF_THREADS]\n"); - printf("[-r AMBIENT_RAD_TEMP:REFERENCE_TEMP]\n"); - printf("\n -h: print this help.\n"); - printf("\nMandatory arguments\n"); - printf("---------\n"); - printf("\n -M MEDIUM.txt:\n"); - printf(" MEDIUM.txt is a text file which contains the description of the\n"); - printf(" media.\n"); - printf("\n -B BOUNDARY.txt:\n"); - printf(" BOUNDARY.txt is a text file which contains the description of the\n"); - printf(" boundary conditions.\n"); - printf("\nExclusive arguments\n"); - printf("-------------------\n"); - printf("\n -p X:Y:Z:TIME:\n"); - printf(" Compute the temperature at the given probe.\n"); - printf(" The probe must be in a medium.\n"); - printf(" If some boundary conditions are time-dependant, TIME cannot be INF.\n"); - printf("\n -P X:Y:Z:TIME:\n"); - printf(" Compute the temperature at the given probe on an interface.\n"); - printf(" The probe must be in a medium and is moved to the closest interface.\n"); - printf(" If some boundary conditions are time-dependant, TIME cannot be INF.\n"); - printf("\n -m MEDIUM_NAME,TIME:\n"); - printf(" Compute the mean temperature in a given medium at a given time.\n"); - printf(" If some boundary conditions are time-dependant, TIME cannot be INF.\n"); - printf("\n -d:\n"); - printf(" Dump the geometry in VTK format with medium front/back id and\n"); - printf(" boundary id.\n"); - printf("\n -b SURFACE.vtk,TIME:\n"); - printf(" Compute the mean temperature on a given 2D shape at a given time.\n"); - printf(" Mean temperature is computed on the front sides of the primitives\n"); - printf(" listed in SURFACE.vtk. These primitives are not added to the geometry,\n"); - printf(" but must be already known. The shape doesn't need to be connex.\n"); - printf(" If some boundary conditions are time-dependant, TIME cannot be INF.\n"); - printf("\n -R t=TIME:spp=SPP:fov=FOV:up=XUP,YUP,ZUP:pos=X,Y,Z:tgt=XT,YT,ZT:img=WxH:\n"); - printf(" The infra-red rendering mode.\n"); - printf(" t is the rendering time (default: INF).\n"); - printf(" spp is the number of samples per pixel (default: 4).\n"); - printf(" fov is the field of view (default: 70 degrees).\n"); - printf(" up is the up direction (default: 0,0,1).\n"); - printf(" pos is the position of camera (default: 1,1,1).\n"); - printf(" tgt is the target (default: 0,0,0).\n"); - printf(" img is the resolution (default: 640x480).\n"); - printf("\nOptionnal arguments\n"); - printf("-------------------\n"); - printf("\n -g:\n"); - printf(" Compute the green function.\n"); - printf(" Must be used in conjonction with one the -p, -m or -b options.\n"); - printf(" Provided TIME is silently ignored.\n"); - printf("\n -s SCALE_FACTOR:\n"); - printf(" default value: 1.0.\n"); - printf("\n -D { all | error | success }:\n"); - printf(" dump paths in VTK format.\n"); - printf("\n -n NUM_OF_REALIZATIONS:\n"); - printf(" Number of Monte-Carlo realizations.\n"); - printf(" default value: 10000.\n"); - printf("\n -t NUM_OF_THREADS:\n"); - printf(" Number of threads; default value is all threads available.\n"); - printf("\n -r AMBIENT_RAD_TEMP,REFERENCE_TEMP:\n"); - printf(" Parameters for the radiative transfer.\n"); - printf(" AMBIENT_RAD_TEMP is the ambient radiative temperature .\n"); - printf(" REFERENCE_TEMP is the temperature used for the linearization\n"); - printf(" of the radiative transfer.\n"); -} - - -static INLINE res_T -parse_args(const int argc, char** argv, struct args* args) -{ - int opt = 0; - size_t len = 0; - res_T res = RES_OK; - - if (argc == 1) { - print_help(argv[0]); - res = RES_BAD_ARG; - goto error; - } - - opterr = 0; /* No default error messages */ - while((opt = getopt(argc, argv, "hgn:t:b:B:M:m:p:P:dD:s:r:R:")) != -1) { - switch(opt) { - case '?': /* Unreconised option */ - res = RES_BAD_ARG; - fprintf(stderr, "Invalid option -%c\n", optopt); - goto error; - - case 'h': - print_help(argv[0]); - args->just_help = 1; - goto exit; - - case 'g': - args->mode |= GREEN_MODE; - break; - - case 'n': { - unsigned long n; - res = cstr_to_ulong(optarg, &n); - if (res != RES_OK || n == 0) { - res = RES_BAD_ARG; - fprintf(stderr, "Invalid argument -%c %s\n", opt, optarg); - goto error; - } - args->N = n; - break; - } - - case 'B': - args->bc_filename = optarg; - break; - - case 'M': - args->medium_filename = optarg; - break; - - case 't': - res = cstr_to_uint(optarg, &args->nthreads); - if (res != RES_OK - || args->nthreads <= 0){ - res = RES_BAD_ARG; - fprintf(stderr, "Invalid argument -%c %s\n", opt, optarg); - goto error; - } - break; - - case 'p': - if (args->mode & COMPUTE_MODES) { - res = RES_BAD_ARG; - fprintf(stderr, "Cannot specify multiple modes: -%c and -%c\n", - (char)opt, mode_option(args->mode)); - goto error; - } - args->mode |= PROBE_COMPUTE; - cstr_to_list_double(optarg, ',', args->u.probe, &len, 4); - if (len != 4 - || res != RES_OK - || args->u.probe[3] < 0) - { - res = RES_BAD_ARG; - fprintf(stderr, "Invalid argument -%c %s\n", opt, optarg); - goto error; - } - break; - - case 'P': - if (args->mode & COMPUTE_MODES) { - res = RES_BAD_ARG; - fprintf(stderr, "Cannot specify multiple modes: -%c and -%c\n", - (char)opt, mode_option(args->mode)); - goto error; - } - args->mode |= PROBE_COMPUTE_ON_INTERFACE; - cstr_to_list_double(optarg, ',', args->u.probe, &len, 4); - if(len != 4 - || res != RES_OK - || args->u.probe[3] < 0) - { - res = RES_BAD_ARG; - fprintf(stderr, "Invalid argument -%c %s\n", opt, optarg); - goto error; - } - break; - - case 'b': { - int err = 0; - char *ptr; - if (args->mode & COMPUTE_MODES) { - res = RES_BAD_ARG; - fprintf(stderr, "Cannot specify multiple modes: -%c and -%c\n", - (char)opt, mode_option(args->mode)); - goto error; - } - args->mode |= BOUNDARY_COMPUTE; - ptr = strrchr(optarg, ','); - /* Single ',' allowed */ - if (!ptr || ptr != strchr(optarg, ',')) - err = 1; - else { - *ptr = '\0'; - ptr++; - args->solve_boundary_filename = optarg; - err = (RES_OK != cstr_to_double(ptr, args->u.probe + 3) || args->u.probe[3] < 0); - } - if (err) { - res = RES_BAD_ARG; - fprintf(stderr, "Invalid argument -%c %s\n", opt, optarg); - goto error; - } - break; - } - - case 'm': { - char* ptr; - if (args->mode & COMPUTE_MODES) { - res = RES_BAD_ARG; - fprintf(stderr, "Cannot specify multiple modes: -%c and -%c\n", - (char)opt, mode_option(args->mode)); - goto error; - } - args->mode |= MEDIUM_COMPUTE; - ptr = strpbrk(optarg, ","); - if (! ptr || ptr == optarg) - res = RES_BAD_ARG; - else res = cstr_to_double(ptr+1, args->u.probe+3); - if (res != RES_OK) { - fprintf(stderr, "Invalid argument -%c %s\n", opt, optarg); - goto error; - } - *ptr = '\0'; - args->medium_name = optarg; - break; - } - - case 'd': - if (args->mode & COMPUTE_MODES) { - res = RES_BAD_ARG; - fprintf(stderr, "Cannot specify multiple modes: -%c and -%c\n", - (char)opt, mode_option(args->mode)); - goto error; - } - args->mode |= DUMP_VTK; - break; - - case 'D': - if (0 == strcmp(optarg, "all")) { - args->dump_paths = DUMP_ALL; - } else if(0 == strcmp(optarg, "error")) { - args->dump_paths = DUMP_ERROR; - } else if(0 == strcmp(optarg, "success")) { - args->dump_paths = DUMP_SUCCESS; - } else { - res = RES_BAD_ARG; - fprintf(stderr, "Invalid argument -%c %s\n", opt, optarg); - goto error; - } - break; - - case 's': - cstr_to_double(optarg, &args->scale_factor); - if (res != RES_OK - || args->scale_factor <=0){ - res = RES_BAD_ARG; - fprintf(stderr, "Invalid argument -%c %s\n", opt, optarg); - goto error; - } - break; - - case 'r': - cstr_to_list_double(optarg, ',', args->radiative_temp, &len, 2); - if (res != RES_OK - || len != 2 - || args->radiative_temp[0] <=0 - || args->radiative_temp[1] <=0){ - res = RES_BAD_ARG; - fprintf(stderr, "Invalid argument -%c %s\n", opt, optarg); - goto error; - } - break; - - case 'R': - if (args->mode & COMPUTE_MODES) { - res = RES_BAD_ARG; - fprintf(stderr, "Cannot specify multiple modes: -%c and -%c\n", - (char)opt, mode_option(args->mode)); - goto error; - } - args->mode |= IR_COMPUTE; - args->camera = optarg; - break; - } - } - -if (!args->medium_filename){ - fprintf(stderr, "Argument required -m\n"); - res = RES_BAD_ARG; - goto error; -} - -if (!args->bc_filename){ - fprintf(stderr, "Argument required -b\n"); - res = RES_BAD_ARG; - goto error; -} - -exit: - return res; -error: - goto exit; -} - -#endif /*ARGS_H*/ diff --git a/src/b_T_1.stl b/src/b_T_1.stl @@ -1,16 +0,0 @@ -solid shape, STL ascii file, created with Open CASCADE Technology - facet normal -0.000000e+00 0.000000e+00 -1.000000e+00 - outer loop - vertex 1.000000e+00 1.000000e+00 0.000000e+00 - vertex 0.000000e+00 0.000000e+00 0.000000e+00 - vertex 0.000000e+00 1.000000e+00 0.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 -0.000000e+00 -1.000000e+00 - outer loop - vertex 1.000000e+00 1.000000e+00 0.000000e+00 - vertex 1.000000e+00 0.000000e+00 0.000000e+00 - vertex 0.000000e+00 0.000000e+00 0.000000e+00 - endloop - endfacet -endsolid shape diff --git a/src/b_T_2.stl b/src/b_T_2.stl @@ -1,30 +0,0 @@ -solid shape, STL ascii file, created with Open CASCADE Technology - facet normal 0.000000e+00 0.000000e+00 1.000000e+00 - outer loop - vertex 1.000000e+00 5.000000e-01 2.000000e+00 - vertex 0.000000e+00 5.000000e-01 2.000000e+00 - vertex 0.000000e+00 0.000000e+00 2.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 0.000000e+00 1.000000e+00 - outer loop - vertex 1.000000e+00 5.000000e-01 2.000000e+00 - vertex 0.000000e+00 0.000000e+00 2.000000e+00 - vertex 1.000000e+00 0.000000e+00 2.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 0.000000e+00 1.000000e+00 - outer loop - vertex 1.000000e+00 1.000000e+00 2.000000e+00 - vertex 0.000000e+00 1.000000e+00 2.000000e+00 - vertex 0.000000e+00 5.000000e-01 2.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 0.000000e+00 1.000000e+00 - outer loop - vertex 1.000000e+00 1.000000e+00 2.000000e+00 - vertex 0.000000e+00 5.000000e-01 2.000000e+00 - vertex 1.000000e+00 5.000000e-01 2.000000e+00 - endloop - endfacet -endsolid shape diff --git a/src/b_h_1.stl b/src/b_h_1.stl @@ -1,156 +0,0 @@ -solid shape, STL ascii file, created with Open CASCADE Technology - facet normal 1.000000e+00 0.000000e+00 0.000000e+00 - outer loop - vertex 1.000000e+00 1.000000e+00 1.000000e+00 - vertex 1.000000e+00 5.000000e-01 2.000000e+00 - vertex 1.000000e+00 5.000000e-01 1.000000e+00 - endloop - endfacet - facet normal 1.000000e+00 0.000000e+00 -0.000000e+00 - outer loop - vertex 1.000000e+00 1.000000e+00 1.000000e+00 - vertex 1.000000e+00 1.000000e+00 2.000000e+00 - vertex 1.000000e+00 5.000000e-01 2.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 -1.000000e+00 0.000000e+00 - outer loop - vertex 1.000000e+00 0.000000e+00 1.000000e+00 - vertex 1.000000e+00 0.000000e+00 2.000000e+00 - vertex 0.000000e+00 0.000000e+00 2.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 -1.000000e+00 0.000000e+00 - outer loop - vertex 1.000000e+00 0.000000e+00 1.000000e+00 - vertex 0.000000e+00 0.000000e+00 2.000000e+00 - vertex 0.000000e+00 0.000000e+00 1.000000e+00 - endloop - endfacet - facet normal 1.000000e+00 0.000000e+00 0.000000e+00 - outer loop - vertex 1.000000e+00 5.000000e-01 1.000000e+00 - vertex 1.000000e+00 0.000000e+00 2.000000e+00 - vertex 1.000000e+00 0.000000e+00 1.000000e+00 - endloop - endfacet - facet normal 1.000000e+00 0.000000e+00 -0.000000e+00 - outer loop - vertex 1.000000e+00 5.000000e-01 1.000000e+00 - vertex 1.000000e+00 5.000000e-01 2.000000e+00 - vertex 1.000000e+00 0.000000e+00 2.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 -1.000000e+00 -0.000000e+00 - outer loop - vertex 1.000000e+00 0.000000e+00 1.000000e+00 - vertex 0.000000e+00 0.000000e+00 0.000000e+00 - vertex 1.000000e+00 0.000000e+00 0.000000e+00 - endloop - endfacet - facet normal -0.000000e+00 -1.000000e+00 0.000000e+00 - outer loop - vertex 1.000000e+00 0.000000e+00 1.000000e+00 - vertex 0.000000e+00 0.000000e+00 1.000000e+00 - vertex 0.000000e+00 0.000000e+00 0.000000e+00 - endloop - endfacet - facet normal 1.000000e+00 0.000000e+00 0.000000e+00 - outer loop - vertex 1.000000e+00 5.000000e-01 1.000000e+00 - vertex 1.000000e+00 0.000000e+00 0.000000e+00 - vertex 1.000000e+00 1.000000e+00 0.000000e+00 - endloop - endfacet - facet normal 1.000000e+00 -0.000000e+00 0.000000e+00 - outer loop - vertex 1.000000e+00 5.000000e-01 1.000000e+00 - vertex 1.000000e+00 1.000000e+00 0.000000e+00 - vertex 1.000000e+00 1.000000e+00 1.000000e+00 - endloop - endfacet - facet normal 1.000000e+00 -0.000000e+00 0.000000e+00 - outer loop - vertex 1.000000e+00 0.000000e+00 1.000000e+00 - vertex 1.000000e+00 0.000000e+00 0.000000e+00 - vertex 1.000000e+00 5.000000e-01 1.000000e+00 - endloop - endfacet - facet normal -1.000000e+00 0.000000e+00 0.000000e+00 - outer loop - vertex 0.000000e+00 1.000000e+00 1.000000e+00 - vertex 0.000000e+00 5.000000e-01 1.000000e+00 - vertex 0.000000e+00 5.000000e-01 2.000000e+00 - endloop - endfacet - facet normal -1.000000e+00 0.000000e+00 0.000000e+00 - outer loop - vertex 0.000000e+00 1.000000e+00 1.000000e+00 - vertex 0.000000e+00 5.000000e-01 2.000000e+00 - vertex 0.000000e+00 1.000000e+00 2.000000e+00 - endloop - endfacet - facet normal -1.000000e+00 0.000000e+00 0.000000e+00 - outer loop - vertex 0.000000e+00 5.000000e-01 1.000000e+00 - vertex 0.000000e+00 0.000000e+00 1.000000e+00 - vertex 0.000000e+00 0.000000e+00 2.000000e+00 - endloop - endfacet - facet normal -1.000000e+00 0.000000e+00 0.000000e+00 - outer loop - vertex 0.000000e+00 5.000000e-01 1.000000e+00 - vertex 0.000000e+00 0.000000e+00 2.000000e+00 - vertex 0.000000e+00 5.000000e-01 2.000000e+00 - endloop - endfacet - facet normal -1.000000e+00 0.000000e+00 -0.000000e+00 - outer loop - vertex 0.000000e+00 5.000000e-01 1.000000e+00 - vertex 0.000000e+00 1.000000e+00 0.000000e+00 - vertex 0.000000e+00 0.000000e+00 0.000000e+00 - endloop - endfacet - facet normal -1.000000e+00 0.000000e+00 0.000000e+00 - outer loop - vertex 0.000000e+00 5.000000e-01 1.000000e+00 - vertex 0.000000e+00 1.000000e+00 1.000000e+00 - vertex 0.000000e+00 1.000000e+00 0.000000e+00 - endloop - endfacet - facet normal -1.000000e+00 0.000000e+00 0.000000e+00 - outer loop - vertex 0.000000e+00 0.000000e+00 1.000000e+00 - vertex 0.000000e+00 5.000000e-01 1.000000e+00 - vertex 0.000000e+00 0.000000e+00 0.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 1.000000e+00 0.000000e+00 - outer loop - vertex 1.000000e+00 1.000000e+00 1.000000e+00 - vertex 1.000000e+00 1.000000e+00 0.000000e+00 - vertex 0.000000e+00 1.000000e+00 0.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 1.000000e+00 0.000000e+00 - outer loop - vertex 1.000000e+00 1.000000e+00 1.000000e+00 - vertex 0.000000e+00 1.000000e+00 0.000000e+00 - vertex 0.000000e+00 1.000000e+00 1.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 1.000000e+00 -0.000000e+00 - outer loop - vertex 1.000000e+00 1.000000e+00 1.000000e+00 - vertex 0.000000e+00 1.000000e+00 2.000000e+00 - vertex 1.000000e+00 1.000000e+00 2.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 1.000000e+00 0.000000e+00 - outer loop - vertex 1.000000e+00 1.000000e+00 1.000000e+00 - vertex 0.000000e+00 1.000000e+00 1.000000e+00 - vertex 0.000000e+00 1.000000e+00 2.000000e+00 - endloop - endfacet -endsolid shape diff --git a/src/bc.txt b/src/bc.txt @@ -1,5 +0,0 @@ -#STL_filename h h_coeff T_env(x,y,z,t) | STL_filename T T_value(x,y,z,t) | STL_filename F F_value(x,y,z,t) -b_h_1.stl h 0 "300" -#b_h_1.stl F "100" -b_T_1.stl T "300" -b_T_2.stl T "300" diff --git a/src/m_1.stl b/src/m_1.stl @@ -1,114 +0,0 @@ -solid shape, STL ascii file, created with Open CASCADE Technology - facet normal -1.000000e+00 0.000000e+00 -0.000000e+00 - outer loop - vertex 0.000000e+00 5.000000e-01 1.000000e+00 - vertex 0.000000e+00 1.000000e+00 0.000000e+00 - vertex 0.000000e+00 0.000000e+00 0.000000e+00 - endloop - endfacet - facet normal -1.000000e+00 0.000000e+00 0.000000e+00 - outer loop - vertex 0.000000e+00 5.000000e-01 1.000000e+00 - vertex 0.000000e+00 1.000000e+00 1.000000e+00 - vertex 0.000000e+00 1.000000e+00 0.000000e+00 - endloop - endfacet - facet normal -1.000000e+00 0.000000e+00 0.000000e+00 - outer loop - vertex 0.000000e+00 0.000000e+00 1.000000e+00 - vertex 0.000000e+00 5.000000e-01 1.000000e+00 - vertex 0.000000e+00 0.000000e+00 0.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 -1.000000e+00 -0.000000e+00 - outer loop - vertex 1.000000e+00 0.000000e+00 1.000000e+00 - vertex 0.000000e+00 0.000000e+00 0.000000e+00 - vertex 1.000000e+00 0.000000e+00 0.000000e+00 - endloop - endfacet - facet normal -0.000000e+00 -1.000000e+00 0.000000e+00 - outer loop - vertex 1.000000e+00 0.000000e+00 1.000000e+00 - vertex 0.000000e+00 0.000000e+00 1.000000e+00 - vertex 0.000000e+00 0.000000e+00 0.000000e+00 - endloop - endfacet - facet normal -0.000000e+00 0.000000e+00 -1.000000e+00 - outer loop - vertex 1.000000e+00 1.000000e+00 0.000000e+00 - vertex 0.000000e+00 0.000000e+00 0.000000e+00 - vertex 0.000000e+00 1.000000e+00 0.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 -0.000000e+00 -1.000000e+00 - outer loop - vertex 1.000000e+00 1.000000e+00 0.000000e+00 - vertex 1.000000e+00 0.000000e+00 0.000000e+00 - vertex 0.000000e+00 0.000000e+00 0.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 0.000000e+00 1.000000e+00 - outer loop - vertex 1.000000e+00 5.000000e-01 1.000000e+00 - vertex 0.000000e+00 5.000000e-01 1.000000e+00 - vertex 0.000000e+00 0.000000e+00 1.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 0.000000e+00 1.000000e+00 - outer loop - vertex 1.000000e+00 5.000000e-01 1.000000e+00 - vertex 0.000000e+00 0.000000e+00 1.000000e+00 - vertex 1.000000e+00 0.000000e+00 1.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 1.000000e+00 0.000000e+00 - outer loop - vertex 1.000000e+00 1.000000e+00 1.000000e+00 - vertex 1.000000e+00 1.000000e+00 0.000000e+00 - vertex 0.000000e+00 1.000000e+00 0.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 1.000000e+00 0.000000e+00 - outer loop - vertex 1.000000e+00 1.000000e+00 1.000000e+00 - vertex 0.000000e+00 1.000000e+00 0.000000e+00 - vertex 0.000000e+00 1.000000e+00 1.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 0.000000e+00 1.000000e+00 - outer loop - vertex 1.000000e+00 1.000000e+00 1.000000e+00 - vertex 0.000000e+00 1.000000e+00 1.000000e+00 - vertex 0.000000e+00 5.000000e-01 1.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 0.000000e+00 1.000000e+00 - outer loop - vertex 1.000000e+00 1.000000e+00 1.000000e+00 - vertex 0.000000e+00 5.000000e-01 1.000000e+00 - vertex 1.000000e+00 5.000000e-01 1.000000e+00 - endloop - endfacet - facet normal 1.000000e+00 0.000000e+00 0.000000e+00 - outer loop - vertex 1.000000e+00 5.000000e-01 1.000000e+00 - vertex 1.000000e+00 0.000000e+00 0.000000e+00 - vertex 1.000000e+00 1.000000e+00 0.000000e+00 - endloop - endfacet - facet normal 1.000000e+00 -0.000000e+00 0.000000e+00 - outer loop - vertex 1.000000e+00 5.000000e-01 1.000000e+00 - vertex 1.000000e+00 1.000000e+00 0.000000e+00 - vertex 1.000000e+00 1.000000e+00 1.000000e+00 - endloop - endfacet - facet normal 1.000000e+00 -0.000000e+00 0.000000e+00 - outer loop - vertex 1.000000e+00 0.000000e+00 1.000000e+00 - vertex 1.000000e+00 0.000000e+00 0.000000e+00 - vertex 1.000000e+00 5.000000e-01 1.000000e+00 - endloop - endfacet -endsolid shape diff --git a/src/m_2.stl b/src/m_2.stl @@ -1,86 +0,0 @@ -solid shape, STL ascii file, created with Open CASCADE Technology - facet normal -1.000000e+00 0.000000e+00 0.000000e+00 - outer loop - vertex 0.000000e+00 5.000000e-01 1.000000e+00 - vertex 0.000000e+00 0.000000e+00 1.000000e+00 - vertex 0.000000e+00 0.000000e+00 2.000000e+00 - endloop - endfacet - facet normal -1.000000e+00 0.000000e+00 0.000000e+00 - outer loop - vertex 0.000000e+00 5.000000e-01 1.000000e+00 - vertex 0.000000e+00 0.000000e+00 2.000000e+00 - vertex 0.000000e+00 5.000000e-01 2.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 -1.000000e+00 0.000000e+00 - outer loop - vertex 1.000000e+00 0.000000e+00 1.000000e+00 - vertex 1.000000e+00 0.000000e+00 2.000000e+00 - vertex 0.000000e+00 0.000000e+00 2.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 -1.000000e+00 0.000000e+00 - outer loop - vertex 1.000000e+00 0.000000e+00 1.000000e+00 - vertex 0.000000e+00 0.000000e+00 2.000000e+00 - vertex 0.000000e+00 0.000000e+00 1.000000e+00 - endloop - endfacet - facet normal -0.000000e+00 0.000000e+00 -1.000000e+00 - outer loop - vertex 1.000000e+00 5.000000e-01 1.000000e+00 - vertex 0.000000e+00 0.000000e+00 1.000000e+00 - vertex 0.000000e+00 5.000000e-01 1.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 -0.000000e+00 -1.000000e+00 - outer loop - vertex 1.000000e+00 5.000000e-01 1.000000e+00 - vertex 1.000000e+00 0.000000e+00 1.000000e+00 - vertex 0.000000e+00 0.000000e+00 1.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 0.000000e+00 1.000000e+00 - outer loop - vertex 1.000000e+00 5.000000e-01 2.000000e+00 - vertex 0.000000e+00 5.000000e-01 2.000000e+00 - vertex 0.000000e+00 0.000000e+00 2.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 0.000000e+00 1.000000e+00 - outer loop - vertex 1.000000e+00 5.000000e-01 2.000000e+00 - vertex 0.000000e+00 0.000000e+00 2.000000e+00 - vertex 1.000000e+00 0.000000e+00 2.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 1.000000e+00 0.000000e+00 - outer loop - vertex 0.000000e+00 5.000000e-01 1.000000e+00 - vertex 1.000000e+00 5.000000e-01 2.000000e+00 - vertex 1.000000e+00 5.000000e-01 1.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 1.000000e+00 0.000000e+00 - outer loop - vertex 0.000000e+00 5.000000e-01 1.000000e+00 - vertex 0.000000e+00 5.000000e-01 2.000000e+00 - vertex 1.000000e+00 5.000000e-01 2.000000e+00 - endloop - endfacet - facet normal 1.000000e+00 0.000000e+00 0.000000e+00 - outer loop - vertex 1.000000e+00 5.000000e-01 1.000000e+00 - vertex 1.000000e+00 0.000000e+00 2.000000e+00 - vertex 1.000000e+00 0.000000e+00 1.000000e+00 - endloop - endfacet - facet normal 1.000000e+00 0.000000e+00 -0.000000e+00 - outer loop - vertex 1.000000e+00 5.000000e-01 1.000000e+00 - vertex 1.000000e+00 5.000000e-01 2.000000e+00 - vertex 1.000000e+00 0.000000e+00 2.000000e+00 - endloop - endfacet -endsolid shape diff --git a/src/m_3.stl b/src/m_3.stl @@ -1,86 +0,0 @@ -solid shape, STL ascii file, created with Open CASCADE Technology - facet normal -1.000000e+00 0.000000e+00 0.000000e+00 - outer loop - vertex 0.000000e+00 1.000000e+00 1.000000e+00 - vertex 0.000000e+00 5.000000e-01 1.000000e+00 - vertex 0.000000e+00 5.000000e-01 2.000000e+00 - endloop - endfacet - facet normal -1.000000e+00 0.000000e+00 0.000000e+00 - outer loop - vertex 0.000000e+00 1.000000e+00 1.000000e+00 - vertex 0.000000e+00 5.000000e-01 2.000000e+00 - vertex 0.000000e+00 1.000000e+00 2.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 -1.000000e+00 0.000000e+00 - outer loop - vertex 0.000000e+00 5.000000e-01 1.000000e+00 - vertex 1.000000e+00 5.000000e-01 1.000000e+00 - vertex 1.000000e+00 5.000000e-01 2.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 -1.000000e+00 0.000000e+00 - outer loop - vertex 0.000000e+00 5.000000e-01 1.000000e+00 - vertex 1.000000e+00 5.000000e-01 2.000000e+00 - vertex 0.000000e+00 5.000000e-01 2.000000e+00 - endloop - endfacet - facet normal -0.000000e+00 0.000000e+00 -1.000000e+00 - outer loop - vertex 1.000000e+00 1.000000e+00 1.000000e+00 - vertex 0.000000e+00 5.000000e-01 1.000000e+00 - vertex 0.000000e+00 1.000000e+00 1.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 -0.000000e+00 -1.000000e+00 - outer loop - vertex 1.000000e+00 1.000000e+00 1.000000e+00 - vertex 1.000000e+00 5.000000e-01 1.000000e+00 - vertex 0.000000e+00 5.000000e-01 1.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 0.000000e+00 1.000000e+00 - outer loop - vertex 1.000000e+00 1.000000e+00 2.000000e+00 - vertex 0.000000e+00 1.000000e+00 2.000000e+00 - vertex 0.000000e+00 5.000000e-01 2.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 0.000000e+00 1.000000e+00 - outer loop - vertex 1.000000e+00 1.000000e+00 2.000000e+00 - vertex 0.000000e+00 5.000000e-01 2.000000e+00 - vertex 1.000000e+00 5.000000e-01 2.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 1.000000e+00 -0.000000e+00 - outer loop - vertex 1.000000e+00 1.000000e+00 1.000000e+00 - vertex 0.000000e+00 1.000000e+00 2.000000e+00 - vertex 1.000000e+00 1.000000e+00 2.000000e+00 - endloop - endfacet - facet normal 0.000000e+00 1.000000e+00 0.000000e+00 - outer loop - vertex 1.000000e+00 1.000000e+00 1.000000e+00 - vertex 0.000000e+00 1.000000e+00 1.000000e+00 - vertex 0.000000e+00 1.000000e+00 2.000000e+00 - endloop - endfacet - facet normal 1.000000e+00 0.000000e+00 0.000000e+00 - outer loop - vertex 1.000000e+00 1.000000e+00 1.000000e+00 - vertex 1.000000e+00 5.000000e-01 2.000000e+00 - vertex 1.000000e+00 5.000000e-01 1.000000e+00 - endloop - endfacet - facet normal 1.000000e+00 0.000000e+00 -0.000000e+00 - outer loop - vertex 1.000000e+00 1.000000e+00 1.000000e+00 - vertex 1.000000e+00 1.000000e+00 2.000000e+00 - vertex 1.000000e+00 5.000000e-01 2.000000e+00 - endloop - endfacet -endsolid shape diff --git a/src/main.c b/src/main.c @@ -1,48 +0,0 @@ -/* Copyright (C) 2018 |Meso|Star> (contact@meso-star.com) */ - -#include "stardis-app.h" - -int main(int argc, char** argv){ - - struct args args = ARGS_DEFAULT; - struct stardis stardis = NULL_STARDIS; - size_t memsz = 0; - int err = 0; - res_T res = RES_OK; - - /* Check proper init for ARGS_DEFAULT */ - ASSERT(ARGS_DEFAULT.u.probe[3] == INF); - - res = parse_args(argc, argv, &args); - if (res != RES_OK) goto error; - if (args.just_help) goto exit; - - res = stardis_init(&args, &stardis); - if (res != RES_OK) goto error; - - if (args.mode & DUMP_VTK) { - res = dump_vtk(stdout, &stardis.geometry); - if (res != RES_OK) goto error; - goto exit; - } - - res = stardis_compute(&stardis, args.mode); - if (res != RES_OK) goto error; - -exit: - stardis_release(&stardis); - if(stardis.allocator_initialized - && (memsz = MEM_ALLOCATED_SIZE(&stardis.allocator)) != 0) - { - char dump[4096] = { '\0' }; - MEM_DUMP(&stardis.allocator, dump, sizeof(dump)); - fprintf(stderr, "%s\n", dump); - fprintf(stderr, "Memory leaks: %lu Bytes\n", (unsigned long)memsz); - } - mem_shutdown_proxy_allocator(&stardis.allocator); - CHK(mem_allocated_size() == 0); - return err; -error: - err = -1; - goto exit; -} diff --git a/src/material.txt b/src/material.txt @@ -1,4 +0,0 @@ -#STL_filename lambda rho cp delta emissivity specular_fraction Tinit(x,y,z) volumic_power(x,y,z,t) -m_1.stl 1.0 1.0 1.0 0.05 1.0 0.0 "300" "0" -m_2.stl 1.0 1.0 1.0 0.05 1.0 0.0 "300" "0" -m_3.stl 1.0 1.0 1.0 0.05 1.0 0.0 "300" "0" diff --git a/src/stardis-app.c b/src/stardis-app.c @@ -1,1032 +1,608 @@ -/* Copyright (C) 2018 |Meso|Star> (contact@meso-star.com) */ +/* Copyright (C) 2018-2020 |Meso|Star> (contact@meso-star.com) + * + * This program 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. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ -#define _GNU_SOURCE 1 -#include <string.h> -#include <ctype.h> #include "stardis-app.h" +#include "stardis-output.h" +#include "stardis-compute.h" +#include "stardis-intface.h" +#include "stardis-fluid.h" +#include "stardis-solid.h" + +#include <star/senc3d.h> +#include <star/sg3d_sencXd_helper.h> +#include <star/sg3d_sdisXd_helper.h> + +#include <rsys/str.h> +#include <rsys/text_reader.h> +#include <rsys/logger.h> +#include <rsys/double2.h> +#include <rsys/double3.h> +#include <string.h> -#include <rsys/hash_table.h> -#define HTABLE_NAME descriptions -#define HTABLE_KEY struct description -#define HTABLE_DATA unsigned -#define HTABLE_KEY_FUNCTOR_EQ eq_description -#define HTABLE_KEY_FUNCTOR_HASH hash_description -#include <rsys/hash_table.h> +static const struct dummies DUMMIES_NULL = DUMMIES_NULL__; + +static const struct counts COUNTS_NULL = COUNTS_NULL__; /******************************************************************************* - * + * Local Functions ******************************************************************************/ -static INLINE char* -read_line(char** pb, FILE* stream) -{ - const int chunk_sz = 32; - char* buf = *pb; - size_t idx; - - if(!buf) buf = sa_add(buf, 128); - - do { - /* Read stream until EOL, EOF or up to sa_size(b) */ - if(!fgets(buf, (int)sa_size(buf) + 1, stream)) return NULL; - - /* Ensure that he whole line is read */ - while(!strrchr(buf, '\n') && !feof(stream)) { - /* Add some room to buffer */ - char* more = sa_add(buf, (size_t)chunk_sz); - /* Continue reading chunk_sz more characters until EOL or EOF */ - fgets(more, chunk_sz + 1, stream); - } - /* Search idx of first occurence of comment char or EOL */ - idx = strcspn(buf, "#\n\r"); - /* Remove meaningless text */ - buf[idx] = '\0'; - /* Find first text */ - idx = strspn(buf, " \t"); - } while(buf[idx] == '\0'); /* Skip empty lines */ - *pb = buf; - return buf; -} - -static char** split_line(char* a_str, const char a_delim) -{ - char** result = 0; - size_t count = 0; - char* tmp = a_str; - char* last_comma = 0; - char delim[2]; - delim[0] = a_delim; - delim[1] = 0; - - /* Count how many elements will be extracted. */ - while (*tmp) - { - if (a_delim == *tmp) - { - count++; - last_comma = tmp; - } - tmp++; - } - - /* Add space for trailing token. */ - count += last_comma < (a_str + strlen(a_str) - 1); - - /* Add space for terminating null string so caller - knows where the list of returned strings ends. */ - count++; - - result = malloc(sizeof(char*) * count); - - if (result) - { - size_t idx = 0; - char* token = strtok(a_str, delim); - - while (token) - { - ASSERT(idx < count); -#ifdef COMPILER_CL - *(result + idx++) = _strdup(token); -#else - *(result + idx++) = strdup(token); -#endif - token = strtok(0, delim); - } - ASSERT(idx == count - 1); - *(result + idx) = 0; - } - - return result; -} - -#ifdef COMPILER_GCC -static char* -_strupr(char* s) -{ - char* ptr; - for (ptr = s; *ptr; ++ptr) { - int tmp = toupper(*ptr); - ASSERT(tmp == (char)tmp); - *ptr = (char)tmp; - } - return s; -} -#endif - - -/* Read medium line; should be one of: - * SOLID medium_name STL_filename lambda rho cp delta "Tinit(x,y,z)" [ "volumic_power(x,y,z,t)" ] - * FLUID medium_name STL_filename rho cp "Tinit(x,y,z)" -*/ static res_T -parse_medium_line(char* line, char** stl_filename, struct description* desc) +read_model + (const struct darray_str* model_files, + struct stardis* stardis, + struct dummies* dummies) { - char* tk = NULL; res_T res = RES_OK; - -#define CHK_TOK(x, Name) if ((tk = (x)) == NULL) {\ - fprintf(stderr, "Invalid data (missing token " Name ")\n");\ - res = RES_BAD_ARG;\ - goto exit;\ - } (void)0 - CHK_TOK(_strupr(strtok(line, " ")), "medium type"); - if (strcmp(tk, "SOLID") == 0) { - desc->type = DESC_MAT_SOLID; - desc->d.solid = NULL_SOLID; - - CHK_TOK(strtok(NULL, " "), "medium name"); - if (strlen(tk) >= sizeof(desc->d.solid.name)) { - fprintf(stderr, "Medium name is too long: %s\n", tk); - res = RES_BAD_ARG; - goto exit; - } - strncpy(desc->d.solid.name, tk, sizeof(desc->d.solid.name)); - - CHK_TOK(strtok(NULL, " "), "file name"); - *stl_filename = malloc(strlen(tk) + 1); - strcpy(*stl_filename, tk); - - CHK_TOK(strtok(NULL, " "), "lambda"); - res = cstr_to_double(tk, &desc->d.solid.lambda); - if (res != RES_OK || desc->d.solid.lambda <= 0) { - fprintf(stderr, "Invalid lambda: %s\n", tk); - res = RES_BAD_ARG; - goto exit; - } - CHK_TOK(strtok(NULL, " "), "rho"); - res = cstr_to_double(tk, &desc->d.solid.rho); - if (res != RES_OK || desc->d.solid.rho <= 0) { - fprintf(stderr, "Invalid rho: %s\n", tk); - res = RES_BAD_ARG; - goto exit; - } - CHK_TOK(strtok(NULL, " "), "cp"); - res = cstr_to_double(tk, &desc->d.solid.cp); - if (res != RES_OK || desc->d.solid.cp <= 0) { - fprintf(stderr, "Invalid cp: %s\n", tk); - res = RES_BAD_ARG; - goto exit; - } - CHK_TOK(strtok(NULL, " "), "delta"); - res = cstr_to_double(tk, &desc->d.solid.delta); - if (res != RES_OK || desc->d.solid.delta <= 0) { - fprintf(stderr, "Invalid delta: %s\n", tk); - res = RES_BAD_ARG; - goto exit; - } - /* Depending of the number of spaces before '"' strtok should - * be called once or twice */ - CHK_TOK(strtok(NULL, "\""), "Tinit"); - if (*(tk + strspn(tk, " \t")) == '\0') { - CHK_TOK(strtok(NULL, "\""), "Tinit"); - } - desc->d.solid.Tinit = malloc(strlen(tk) + 1); - strcpy(desc->d.solid.Tinit, tk); - /* Closing " fot Tinit; can return NULL if line ends just after "Tinit" */ - tk = strtok(NULL, "\""); - - /* Volumic Power is optional */ - if (tk && *(tk + strspn(tk, " \t")) == '\0') { - /* Depending of the number of spaces before '"' strtok should - * be called once or twice */ - tk = strtok(NULL, "\""); - } - if (tk) { - desc->d.solid.power = malloc(strlen(tk) + 1); - strcpy(desc->d.solid.power, tk); - /* Can be changed aftwerwards if compiled to constant 0 */ - desc->d.solid.has_power = 1; - } - } - else if (strcmp(tk, "FLUID") == 0) { - desc->type = DESC_MAT_FLUID; - desc->d.fluid = NULL_FLUID; - - CHK_TOK(strtok(NULL, " "), "medium name"); - if (strlen(tk) >= sizeof(desc->d.fluid.name)) { - fprintf(stderr, "Medium name is too long: %s\n", tk); - res = RES_BAD_ARG; - goto exit; - } - strncpy(desc->d.fluid.name, tk, sizeof(desc->d.fluid.name)); - - CHK_TOK(strtok(NULL, " "), "file name"); - *stl_filename = malloc(strlen(tk) + 1); - strcpy(*stl_filename, tk); - - CHK_TOK(strtok(NULL, " "), "rho"); - res = cstr_to_double(tk, &desc->d.fluid.rho); - if (res != RES_OK || desc->d.fluid.rho <= 0) { - fprintf(stderr, "Invalid rho: %s\n", tk); - res = RES_BAD_ARG; - goto exit; - } - CHK_TOK(strtok(NULL, " "), "cp"); - res = cstr_to_double(tk, &desc->d.fluid.cp); - if (res != RES_OK || desc->d.fluid.cp <= 0) { - fprintf(stderr, "Invalid cp: %s\n", tk); - res = RES_BAD_ARG; - goto exit; - } - /* Depending of the number of spaces before '"' strtok should - * be called once or twice */ - CHK_TOK(strtok(NULL, "\""), "Tinit"); - if (*(tk + strspn(tk, " \t")) == '\0') { - CHK_TOK(strtok(NULL, "\""), "Tinit"); - } - desc->d.fluid.Tinit = malloc(strlen(tk) + 1); - strcpy(desc->d.fluid.Tinit, tk); - } - else { - res = RES_BAD_ARG; - fprintf(stderr, "Invalid medium type: %s\n", tk); - } - -#undef CHK_TOK + const struct str* files = NULL; + size_t i; + FILE* f = NULL; + struct txtrdr* txtrdr = NULL; + + ASSERT(model_files && stardis && dummies); + files = darray_str_cdata_get(model_files); + FOR_EACH(i, 0, darray_str_size_get(model_files)) { + const char* name = str_cget(files + i); + f = fopen(name, "r"); + if(!f) { + logger_print(stardis->logger, LOG_ERROR, + "Cannot open model file '%s'\n", + name); + res = RES_IO_ERR; + goto error; + } + txtrdr_stream(stardis->allocator, f, name, '#', &txtrdr); + for(;;) { + char* line; + ERR(txtrdr_read_line(txtrdr)); + line = txtrdr_get_line(txtrdr); + if(!line) break; + ERR(process_model_line(name, line, stardis, dummies)); + } + txtrdr_ref_put(txtrdr); + txtrdr = NULL; + fclose(f); + f = NULL; + } + if(stardis->scale_factor <= 0) + stardis->scale_factor = STARDIS_DEFAULT_SCALE_FACTOR; + logger_print(stardis->logger, LOG_OUTPUT, + "Scaling factor is %g\n", stardis->scale_factor); + + ASSERT(!f && !txtrdr); exit: return res; +error: + if(f) fclose(f); + if(txtrdr) txtrdr_ref_put(txtrdr); + goto exit; } -/* Read boundary line; should be one of: -# H_BOUNDARY_FOR_SOLID Boundary_name STL_filename emissivity specular_fraction hc hc_max "T_env(x,y,z,t)" -# | H_BOUNDARY_FOR_FLUID Boundary_name STL_filename emissivity specular_fraction hc hc_max "T_env(x,y,z,t)" -# | T_BOUNDARY_FOR_SOLID Boundary_name STL_filename "T_value(x,y,z,t)" -# | T_BOUNDARY_FOR_FLUID Boundary_name STL_filename "T_value(x,y,z,t)" hc hc_max -# | F_BOUNDARY_FOR_SOLID Boundary_name STL_filename "F_value(x,y,z,t)" -# ; no F_BOUNDARY_FOR_FLUID -# | SOLID_FLUID_CONNECTION Connection_name STL_filename emissivity specular_fraction hc hc_max - */ -static res_T -parse_boundary_line(char* line, char** stl_filename, struct description* desc) +static struct sdis_interface* +geometry_get_interface + (const size_t itri, void* ctx) { - char* tk = NULL; - int h_solid = 0, h_fluid = 0; - int t_solid = 0, t_fluid = 0; - int f_solid = 0; - int sf_connect = 0; - res_T res = RES_OK; - -#define CHK_TOK(x, Name) if ((tk = (x)) == NULL) {\ - fprintf(stderr, "Invalid data (missing token " Name ")\n");\ - res = RES_BAD_ARG;\ - goto exit;\ - } (void)0 - CHK_TOK(strtok(line, " "), "boundary type"); - h_solid = (0 == strcmp(tk, "H_BOUNDARY_FOR_SOLID")); - if (!h_solid) { - h_fluid = (0 == strcmp(tk, "H_BOUNDARY_FOR_FLUID")); - if (!h_fluid) { - t_solid = (0 == strcmp(tk, "T_BOUNDARY_FOR_SOLID")); - if (!t_solid) { - t_fluid = (0 == strcmp(tk, "T_BOUNDARY_FOR_FLUID")); - if (!t_fluid) { - f_solid = (0 == strcmp(tk, "F_BOUNDARY_FOR_SOLID")); - if (!f_solid) { - sf_connect = (0 == strcmp(tk, "SOLID_FLUID_CONNECTION")); - } - } - } - } - } - - if (h_solid || h_fluid) { - desc->type = h_solid ? DESC_BOUND_H_FOR_SOLID : DESC_BOUND_H_FOR_FLUID; - desc->d.h_boundary = NULL_HBOUND; - - CHK_TOK(strtok(NULL, " "), "boundary name"); - if (strlen(tk) >= sizeof(desc->d.h_boundary.name)) { - fprintf(stderr, "Boundary name is too long: %s\n", tk); - res = RES_BAD_ARG; - goto exit; - } - strncpy(desc->d.h_boundary.name, tk, sizeof(desc->d.h_boundary.name)); - - /* The description is parsed the same way for both types */ - CHK_TOK(strtok(NULL, " "), "file name"); - *stl_filename = malloc(strlen(tk) + 1); - strcpy(*stl_filename, tk); - - CHK_TOK(strtok(NULL, " "), "emissivity"); - res = cstr_to_double(tk, &desc->d.h_boundary.emissivity); - if (res != RES_OK || - desc->d.h_boundary.emissivity < 0 || desc->d.h_boundary.emissivity > 1) { - fprintf(stderr, "Invalid emissivity: %s\n", tk); - res = RES_BAD_ARG; - goto exit; - } - desc->d.h_boundary.has_emissivity = (desc->d.h_boundary.emissivity > 0); - CHK_TOK(strtok(NULL, " "), "specular fraction"); - res = cstr_to_double(tk, &desc->d.h_boundary.specular_fraction); - if (res != RES_OK - || desc->d.h_boundary.specular_fraction < 0.0 - || desc->d.h_boundary.specular_fraction > 1.0) { - fprintf(stderr, "Invalid specular_fraction: %s\n", tk); - res = RES_BAD_ARG; - goto exit; - } - CHK_TOK(strtok(NULL, " "), "hc"); - res = cstr_to_double(tk, &desc->d.h_boundary.hc); - if (res != RES_OK || desc->d.h_boundary.hc < 0) { - fprintf(stderr, "Invalid hc value: %s\n", tk); - res = RES_BAD_ARG; - goto exit; - } - desc->d.h_boundary.has_hc = (desc->d.h_boundary.hc > 0); - CHK_TOK(strtok(NULL, " "), "hc max"); - res = cstr_to_double(tk, &desc->d.h_boundary.hc_max); - if (res != RES_OK - || desc->d.h_boundary.hc_max < desc->d.h_boundary.hc - || desc->d.h_boundary.hc_max < 0) { - fprintf(stderr, "Invalid hc_max value: %s\n", tk); - res = RES_BAD_ARG; - goto exit; - } - /* Depending of the number of spaces before '"' strtok should - * be called once or twice */ - CHK_TOK(strtok(NULL, "\""), "temperature"); - if (*(tk + strspn(tk, " \t")) == '\0') { - CHK_TOK(strtok(NULL, "\""), "temperature"); - } - desc->d.h_boundary.T = malloc(strlen(tk) + 1); - strcpy(desc->d.h_boundary.T, tk); - } - else if (t_solid || t_fluid) { - desc->type = t_solid ? DESC_BOUND_T_FOR_SOLID : DESC_BOUND_T_FOR_FLUID; - desc->d.t_boundary = NULL_TBOUND; - - CHK_TOK(strtok(NULL, " "), "boundary name"); - if (strlen(tk) >= sizeof(desc->d.t_boundary.name)) { - fprintf(stderr, "Boundary name is too long: %s\n", tk); - res = RES_BAD_ARG; - goto exit; - } - strncpy(desc->d.t_boundary.name, tk, sizeof(desc->d.t_boundary.name)); - - /* This part of the description is parsed the same way for both types */ - CHK_TOK(strtok(NULL, " "), "file name"); - *stl_filename = malloc(strlen(tk) + 1); - strcpy(*stl_filename, tk); - - /* Depending of the number of spaces before '"' strtok should - * be called once or twice */ - CHK_TOK(strtok(NULL, "\""), "temperature"); - if (*(tk + strspn(tk, " \t")) == '\0') { - CHK_TOK(strtok(NULL, "\""), "temperature"); - } - desc->d.t_boundary.T = malloc(strlen(tk) + 1); - strcpy(desc->d.t_boundary.T, tk); - - /* Parse hc + hc_max only if fluid */ - if (t_fluid) { - CHK_TOK(strtok(NULL, " "), "hc"); - res = cstr_to_double(tk, &desc->d.t_boundary.hc); - if (res != RES_OK || desc->d.t_boundary.hc < 0) { - fprintf(stderr, "Invalid hc value: %s\n", tk); - res = RES_BAD_ARG; - goto exit; - } - desc->d.t_boundary.has_hc = (desc->d.t_boundary.hc > 0); - CHK_TOK(strtok(NULL, " "), "hc max"); - res = cstr_to_double(tk, &desc->d.t_boundary.hc_max); - if (res != RES_OK - || desc->d.t_boundary.hc_max < desc->d.t_boundary.hc - || desc->d.t_boundary.hc_max < 0) { - fprintf(stderr, "Invalid hc_max value: %s\n", tk); - res = RES_BAD_ARG; - goto exit; - } - } - else desc->d.t_boundary.has_hc = 0; - } - else if (f_solid) { - desc->type = DESC_BOUND_F_FOR_SOLID; - desc->d.f_boundary = NULL_FBOUND; - - CHK_TOK(strtok(NULL, " "), "boundary name"); - if (strlen(tk) >= sizeof(desc->d.f_boundary.name)) { - fprintf(stderr, "Boundary name is too long: %s\n", tk); - res = RES_BAD_ARG; - goto exit; - } - strncpy(desc->d.f_boundary.name, tk, sizeof(desc->d.f_boundary.name)); - - CHK_TOK(strtok(NULL, " "), "file name"); - *stl_filename = malloc(strlen(tk) + 1); - strcpy(*stl_filename, tk); - - /* Depending of the number of spaces before '"' strtok should - * be called once or twice */ - CHK_TOK(strtok(NULL, "\""), "flux"); - if (*(tk + strspn(tk, " \t")) == '\0') { - CHK_TOK(strtok(NULL, "\""), "flux"); - } - desc->d.f_boundary.flux = malloc(strlen(tk) + 1); - strcpy(desc->d.f_boundary.flux, tk); - } - else if (sf_connect) { - desc->type = DESC_SOLID_FLUID_CONNECT; - desc->d.sf_connect = NULL_SFCONNECT; - - CHK_TOK(strtok(NULL, " "), "boundary name"); - if (strlen(tk) >= sizeof(desc->d.sf_connect.name)) { - fprintf(stderr, "Boundary name is too long: %s\n", tk); - res = RES_BAD_ARG; - goto exit; - } - strncpy(desc->d.sf_connect.name, tk, sizeof(desc->d.sf_connect.name)); - - CHK_TOK(strtok(NULL, " "), "file name"); - *stl_filename = malloc(strlen(tk) + 1); - strcpy(*stl_filename, tk); - - CHK_TOK(strtok(NULL, " "), "emissivity"); - res = cstr_to_double(tk, &desc->d.sf_connect.emissivity); - if (res != RES_OK || - desc->d.sf_connect.emissivity < 0 || desc->d.sf_connect.emissivity > 1) { - fprintf(stderr, "Invalid emissivity: %s\n", tk); - res = RES_BAD_ARG; - goto exit; - } - desc->d.sf_connect.has_emissivity = (desc->d.sf_connect.emissivity > 0); - CHK_TOK(strtok(NULL, " "), "specular fraction"); - res = cstr_to_double(tk, &desc->d.sf_connect.specular_fraction); - if (res != RES_OK - || desc->d.sf_connect.specular_fraction < 0.0 - || desc->d.sf_connect.specular_fraction > 1.0) { - fprintf(stderr, "Invalid specular_fraction: %s\n", tk); - res = RES_BAD_ARG; - goto exit; - } - CHK_TOK(strtok(NULL, " "), "hc"); - res = cstr_to_double(tk, &desc->d.sf_connect.hc); - if (res != RES_OK || desc->d.sf_connect.hc < 0) { - fprintf(stderr, "Invalid hc value: %s\n", tk); - res = RES_BAD_ARG; - goto exit; - } - desc->d.sf_connect.has_hc = (desc->d.sf_connect.hc > 0); - CHK_TOK(strtok(NULL, " "), "hc max"); - res = cstr_to_double(tk, &desc->d.h_boundary.hc_max); - if (res != RES_OK - || desc->d.h_boundary.hc_max < desc->d.h_boundary.hc - || desc->d.h_boundary.hc_max < 0) { - fprintf(stderr, "Invalid hc_max value: %s\n", tk); - res = RES_BAD_ARG; - goto exit; - } - } else { - fprintf(stderr, "Invalid boundary type: %s", tk); - res = RES_BAD_ARG; - goto exit; - } - -#undef CHK_TOK -exit: - return res; + struct darray_interface_ptrs* app_interface_data = ctx; + ASSERT(app_interface_data + && itri < darray_interface_ptrs_size_get(app_interface_data)); + return darray_interface_ptrs_cdata_get(app_interface_data)[itri]; } static res_T -read_vertices - (struct sstl_desc* desc, - struct htable_vertex* vertex2id, - unsigned** id2id, - struct geometry* geometry) +check_delta_and_create_solid + (struct stardis* stardis, + struct description* description) { res_T res = RES_OK; - unsigned vtx_index = 0; - - for (vtx_index = 0; vtx_index < desc->vertices_count; ++vtx_index){ - struct vertex vtx = NULL_VERTEX; - unsigned *found_id = NULL; - - vtx.xyz[0] = desc->vertices[3*vtx_index + 0]; - vtx.xyz[1] = desc->vertices[3*vtx_index + 1]; - vtx.xyz[2] = desc->vertices[3*vtx_index + 2]; - found_id = htable_vertex_find(vertex2id, &vtx); - - if (found_id){ - sa_push(*id2id, *found_id); + struct senc3d_enclosure* enc = NULL; + double ratio, delta_range[2] = { DBL_MAX, -DBL_MAX }; + const double acceptance_ratio = 3; + struct senc3d_enclosure_header header; + + ASSERT(stardis && description && description->type == DESC_MAT_SOLID); + + /* Create solid to have ID informed */ + /* Check if delta can fit possible multiple enclosures */ + if(stardis->senc3d_scn) { + /* Due to previous errors, senc3d_scn can be unavailable */ + unsigned e, ecount = 0; + const unsigned desc_id = description->d.solid.desc_id; + + /* The enclosures where created using description ids */ + ERR(senc3d_scene_get_enclosure_count_by_medium(stardis->senc3d_scn, + desc_id, &ecount)); + if(ecount == 0) { + unsigned ccount; + ERR(sg3d_geometry_get_unique_triangles_with_properties_conflict_count( + stardis->geometry.sg3d, &ccount)); + CHK(ccount == 0); /* This solid can only be unused if in conflict*/ } else { - size_t sz = htable_vertex_size_get(vertex2id); - unsigned size; - ASSERT(sz < UINT_MAX); - size = (unsigned)sz; - res = htable_vertex_set(vertex2id, &vtx, &size); - if (res != RES_OK) return res; - sa_push(geometry->vertex, vtx); - sa_push(*id2id, size); - } - } - - return res; -} - -static int -indices_to_key(struct unsigned3* key, const struct unsigned3* indices) -{ - int i, reversed = 0; - FOR_EACH(i, 0, 3) key->data[i] = indices->data[i]; - if (key->data[0] > key->data[1]) { - reversed = !reversed; - SWAP(unsigned, key->data[0], key->data[1]); - } - if (key->data[1] > key->data[2]) { - reversed = !reversed; - SWAP(unsigned, key->data[1], key->data[2]); - } - if (key->data[0] > key->data[1]) { - reversed = !reversed; - SWAP(unsigned, key->data[0], key->data[1]); - } - return reversed; -} - -static res_T -read_triangles - (struct sstl_desc* stl_desc, - struct htable_triangle* triangle2id, - const char* stl_filename, - const unsigned* id2id, - const enum content_type file_type, - const unsigned desc_id, - struct geometry* geometry, - struct solve_boundary* boundary) -{ - res_T res = RES_OK; - unsigned* packed_indices = NULL; - float* packed_normals = NULL; - unsigned tri_index = 0, drop_cpt = 0; - for (tri_index = 0; tri_index < stl_desc->triangles_count; ++tri_index) { - struct triangle tri = NULL_TRIANGLE; - const unsigned *found_id = NULL; - unsigned i, id; - struct unsigned3 key; - int reversed; - int drop = 0; - enum sdis_side current_side = SDIS_FRONT; - unsigned* side_desc_ptr = NULL; - float coord[3][3]; - - FOR_EACH(i, 0, 3) - tri.indices.data[i] = id2id[stl_desc->indices[3*tri_index + i]]; - reversed = indices_to_key(&key, &tri.indices); - - FOR_EACH(i, 0, 3) { - unsigned j; - f3_set(coord[i], geometry->vertex[key.data[i]].xyz); - FOR_EACH(j, 0, i) { - if (f3_eq(coord[i], coord[j])) { - drop = 1; - drop_cpt++; + int external = 0; + FOR_EACH(e, 0, ecount) { + ERR(senc3d_scene_get_enclosure_by_medium(stardis->senc3d_scn, desc_id, + e, &enc)); + ERR(senc3d_enclosure_get_header(enc, &header)); + if(header.is_infinite) { + /* External solid, volume is negative and no delta walk expected */ + external = 1; + } else { + double d = header.volume / (header.area * 6); + ASSERT(d >= 0); + delta_range[0] = MMIN(delta_range[0], d); + delta_range[1] = MMAX(delta_range[1], d); } + ERR(senc3d_enclosure_ref_put(enc)); + enc = NULL; } - } - if (drop) continue; - FOR_EACH(i, 0, 3) { - sa_push(packed_indices, stl_desc->indices[3 * tri_index + i]); - sa_push(packed_normals, stl_desc->normals[3 * tri_index + i]); - } - - /* Find triangle if already known, or insert it in known list */ - found_id = htable_triangle_find(triangle2id, &key); - if (found_id) { - struct triangle* original_tri; - int original_reversed; - id = *found_id; - original_tri = geometry->triangle + id; - original_reversed = indices_to_key(&key, &original_tri->indices); - current_side = (reversed == original_reversed) ? SDIS_FRONT : SDIS_BACK; - ASSERT(id < sa_size(geometry->triangle)); - } else { - size_t sz; - if (file_type == CONTENT_BOUNDARY) { - /* Cannot create new geometry! */ - fprintf(stderr, - "Triangle %u from file %s cannot compute temperature on this surface\n" - "Not part of the model\n", - tri_index, stl_filename); - return RES_BAD_ARG; + if(ecount > 1 || !external) { + ASSERT(0 < delta_range[0] && delta_range[0] <= delta_range[1]); + ratio = delta_range[1] / delta_range[0]; + if(ratio > acceptance_ratio) + logger_print(stardis->logger, LOG_WARNING, + "Solid '%s' is used in %u different enclosures that have different " + "delta requirements.\n", + str_cget(&description->d.solid.name), ecount); + /* Delta needs to be substituted with actual value */ + if(description->d.solid.delta == DELTA_AUTO) { + description->d.solid.delta = delta_range[0]; + logger_print(stardis->logger, LOG_OUTPUT, + "Auto delta for solid '%s' set to %g\n", + str_cget(&description->d.solid.name), description->d.solid.delta); + } else { + int too_small + = (delta_range[0] > description->d.solid.delta * acceptance_ratio); + int too_big + = (delta_range[0] * acceptance_ratio < description->d.solid.delta); + /* Check if user delta is OK */ + if(too_small || too_big) { + logger_print(stardis->logger, LOG_WARNING, + "User delta for solid '%s' seems too %s: %g; " + "auto delta would have set it to %g.\n", + str_cget(&description->d.solid.name), (too_big ? "big" : "small"), + description->d.solid.delta, delta_range[0]); + } + } } - sz = htable_triangle_size_get(triangle2id); - ASSERT(sz == sa_size(geometry->triangle)); - ASSERT(sz < UINT_MAX); - id = (unsigned)sz; - /* Store tri as described by stl_desc regardless of key order */ - res = htable_triangle_set(triangle2id, &key, &id); - if (res != RES_OK) goto error; - sa_push(geometry->triangle, tri); } - - switch (file_type) { - case CONTENT_CONNECTION: - side_desc_ptr = &geometry->triangle[id].connection_description; - break; - case CONTENT_GEOMETRY: - /* Is the triangle in triangle list reversed wrt stl_desc? */ - side_desc_ptr = (current_side == SDIS_FRONT) ? - &geometry->triangle[id].front_description - : &geometry->triangle[id].back_description; - break; - case CONTENT_BOUNDARY: - sa_push(boundary->primitives, id); - sa_push(boundary->sides, current_side); - continue; /* Next triangle */ - break; - default: FATAL("Invalid type.\n"); - } - if (*side_desc_ptr != UINT_MAX && *side_desc_ptr != desc_id) { - /* Already described with a different description! */ - fprintf(stderr, "Triangle %u %s from file %s with 2 different descriptions\n", - tri_index, - (file_type == CONTENT_CONNECTION - ? "connection" : (current_side == SDIS_FRONT ? "front" : "back")), - stl_filename); - print_trg_as_obj(stderr, (const struct vertex*)stl_desc->vertices, stl_desc->indices + (3 * tri_index)); - return RES_BAD_ARG; - } - /* Everithing is OK: store description */ - *side_desc_ptr = desc_id; - } - ASSERT(drop_cpt == 0 || packed_indices != NULL); - if (drop_cpt) { - fprintf(stderr, "File '%s' includes %u degenerate triangles (dropped)\n", - stl_filename, drop_cpt); - /* Keep only valid triangles */ - ASSERT(sa_size(packed_indices) % 3 == 0); - ASSERT(sa_size(packed_indices) == sa_size(packed_normals)); - ASSERT(sa_size(packed_indices) + 3 * drop_cpt == sa_size(stl_desc->indices)); - /* Don't sa_release stl_desc->indices or stl_desc->normals as they have - * their own release mechanism */ - stl_desc->indices = packed_indices; - stl_desc->normals = packed_normals; - stl_desc->triangles_count = sa_size(stl_desc->indices) / 3; - } - else { - sa_release(packed_indices); - sa_release(packed_normals); - packed_indices = NULL; - packed_normals = NULL; } + ERR(create_solver_solid(stardis, &description->d.solid)); end: - sa_release(packed_indices); - sa_release(packed_normals); - - return res; - error: - goto end; -} - -res_T -read_stl - (const unsigned desc_id, - const enum content_type file_type, - const char* stl_filename, - struct htable_vertex* vertex2id, - struct htable_triangle* triangle2id, - struct stardis* stardis) -{ - res_T res = RES_OK; - struct sstl* sstl = NULL; - struct sstl_desc stl_desc; - unsigned* id2id = NULL; - - if (!stl_filename) return RES_BAD_ARG; - - SSTL(create(NULL, &stardis->allocator, 0, &sstl)); - - res = sstl_load(sstl, stl_filename); - if (res != RES_OK) { - fprintf(stderr, "Cannot read STL file: %s\n", stl_filename); - goto error; - } - SSTL(get_desc(sstl, &stl_desc)); - - res = read_vertices(&stl_desc, vertex2id, &id2id, &stardis->geometry); - if (res != RES_OK) goto error; - res = read_triangles(&stl_desc, triangle2id, stl_filename, id2id, file_type, - desc_id, &stardis->geometry, &stardis->boundary); - if (res != RES_OK) goto error; - -end: - sa_release(id2id); - SSTL(ref_put(sstl)); + if(enc) SENC3D(enclosure_ref_put(enc)); return res; error: goto end; } /******************************************************************************* - * + * Public Functions ******************************************************************************/ -static res_T -geometry_analyse - (const char* medium_filename, - const char* bc_filename, - const char* bound_filename, /* Can be NULL */ +res_T +stardis_init + (const struct args* args, + struct logger* logger, + struct mem_allocator* allocator, struct stardis* stardis) { - res_T res = RES_OK; - FILE* input = NULL; - char* line = NULL; - unsigned sz = (unsigned)sa_size(stardis->descriptions); - struct htable_vertex vertex2id; - struct htable_triangle triangle2id; - struct htable_descriptions descriptions; - int tables_initialised = 0; - unsigned desc_id; - unsigned *p_desc; - ASSERT(sz == sa_size(stardis->descriptions)); - - stardis->geometry = NULL_GEOMETRY; - - /* parse medium files */ - input = fopen(medium_filename,"r"); - if (!input){ - fprintf(stderr,"Cannot open %s\n", medium_filename); - res = RES_IO_ERR; - goto error; - } - - htable_vertex_init(&stardis->allocator, &vertex2id); - htable_triangle_init(&stardis->allocator, &triangle2id); - htable_descriptions_init(&stardis->allocator, &descriptions); - tables_initialised = 1; - /* loop on media */ - while (read_line(&line, input)){ - char* stl_filename = NULL; - struct description desc; - - init_description(&desc); - res = parse_medium_line(line, &stl_filename, &desc); - if (res != RES_OK) goto error; - - /* Deduplicate media: find if the same description already exists - * (including name) */ - p_desc = htable_descriptions_find(&descriptions, &desc); - if (!p_desc) { - sa_push(stardis->descriptions, desc); - desc_id = sz++; - ASSERT(sz == sa_size(stardis->descriptions)); - /* Register new medium description */ - res = htable_descriptions_set(&descriptions, &desc, &desc_id); - if (res != RES_OK) goto error; - } else { - fprintf(stderr, "Duplicate media description found: deduplicated.\n"); - desc_id = *p_desc; + res_T tmp_res, res = RES_OK; + struct sg3d_sdisXd_scene_create_context create_context; + struct dummies dummies = DUMMIES_NULL; + struct htable_intface htable_interfaces; + struct str str; + unsigned i, vcount, tcount, ocount, count; + int is_for_compute; + + ASSERT(args && logger && allocator && stardis); + + str_init(allocator, &str); + /* Init everithing that cannot fail */ + stardis->logger = logger; + stardis->allocator = allocator; + htable_intface_init(stardis->allocator, &htable_interfaces); + darray_descriptions_init(stardis->allocator, &stardis->descriptions); + d3_set(stardis->probe, args->pos_and_time); + d2_set(stardis->time_range, args->pos_and_time + 3); + stardis->dev = NULL; + stardis->sdis_scn = NULL; + stardis->senc3d_scn = NULL; + stardis->mode = args->mode; + stardis->counts = COUNTS_NULL; + init_camera(&stardis->camera); + str_init(stardis->allocator, &stardis->solve_name); + str_init(stardis->allocator, &stardis->paths_filename); + str_init(stardis->allocator, &stardis->bin_green_filename); + str_init(stardis->allocator, &stardis->end_paths_filename); + str_init(stardis->allocator, &stardis->chunks_prefix); + darray_size_t_init(stardis->allocator, &stardis->compute_surface.primitives); + darray_sides_init(stardis->allocator, &stardis->compute_surface.sides); + darray_uint_init(stardis->allocator, &stardis->compute_surface.err_triangles); + stardis->samples = args->samples; + stardis->nthreads = args->nthreads; + stardis->scale_factor = -1; /* invalid value */ + stardis->ambient_temp = args->ambient_temp; + stardis->ref_temp = args->ref_temp; + stardis->dump_paths = SDIS_HEAT_PATH_NONE; + if(args->dump_paths & DUMP_ERROR) + stardis->dump_paths |= SDIS_HEAT_PATH_FAILURE; + if(args->dump_paths & DUMP_SUCCESS) + stardis->dump_paths |= SDIS_HEAT_PATH_SUCCESS; + stardis->next_medium_id = 0; + stardis->undefined_medium_behind_boundary_id = SENC3D_UNSPECIFIED_MEDIUM; + stardis->verbose = args->verbose; + darray_media_ptr_init(stardis->allocator, &stardis->media); + + /* If a dump is expected, we won't process any computation */ + is_for_compute = + (stardis->mode & COMPUTE_MODES) && !(stardis->mode & MODE_DUMP_VTK); + + ERR(sdis_device_create(stardis->logger, stardis->allocator, stardis->nthreads, + args->verbose, &stardis->dev)); + + ERR(init_geometry(stardis->logger, stardis->allocator, stardis->verbose, + &stardis->geometry)); + + if(args->mode & MODE_IR_COMPUTE) { + ERR(parse_camera(stardis->logger, args->camera, stardis)); + } + else if(args->mode & MODE_MEDIUM_COMPUTE) { + ERR(str_set(&stardis->solve_name, args->medium_name)); + } + else if(args->mode & SURFACE_COMPUTE_MODES) { + ERR(str_set(&stardis->solve_name, args->solve_filename)); + } + else if(args->mode & MODE_DUMP_C_CHUNKS) { + ERR(str_set(&stardis->chunks_prefix, args->chunks_prefix)); + } + ERR(read_model(&args->model_files, stardis, &dummies)); + + create_context.geometry = stardis->geometry.sg3d; + create_context.app_interface_getter = geometry_get_interface; + create_context.app_interface_data = &stardis->geometry.interf_bytrg; + ERR(sg3d_geometry_get_unique_vertices_count(stardis->geometry.sg3d, &vcount)); + ERR(sg3d_geometry_get_unique_triangles_count(stardis->geometry.sg3d, &tcount)); + + ERR(sg3d_geometry_validate_properties(stardis->geometry.sg3d, + validate_properties, stardis)); + ERR(sg3d_geometry_get_unique_triangles_with_properties_conflict_count( + stardis->geometry.sg3d, &count)); + if(count) { + logger_print(stardis->logger, (is_for_compute ? LOG_ERROR : LOG_WARNING), + "Property conflicts found in the model (%u triangles).\n", count); + if(is_for_compute) { + res = RES_BAD_ARG; + goto error; + } + } + + /* If computation is on a compute surface, read it */ + if(args->mode & SURFACE_COMPUTE_MODES) { + unsigned save_count = count; + ASSERT(!str_is_empty(&stardis->solve_name)); + ERR(read_compute_surface(stardis)); + /* Check compute surface */ + ERR(sg3d_geometry_validate_properties(stardis->geometry.sg3d, + validate_properties, stardis)); + ERR(sg3d_geometry_get_unique_triangles_with_properties_conflict_count( + stardis->geometry.sg3d, &count)); + ASSERT(count >= save_count); + if(save_count != count) { + logger_print(stardis->logger, (is_for_compute ? LOG_ERROR : LOG_WARNING), + "Invalid compute region defined by file '%s'.\n", + str_cget(&stardis->solve_name)); + logger_print(stardis->logger, (is_for_compute ? LOG_ERROR : LOG_WARNING), + "The file contains %u triangles not in the model.\n", count - save_count); + if(is_for_compute) { + res = RES_BAD_ARG; + goto error; + } } - - res = read_stl(sz-1, CONTENT_GEOMETRY, stl_filename, &vertex2id, - &triangle2id, stardis); - if (stl_filename) free(stl_filename); - if (res != RES_OK) goto error; } - fclose(input); - input = NULL; - /* parse boundary files */ - input = fopen(bc_filename,"r"); - if (!input){ - fprintf(stderr,"Cannot open %s\n", bc_filename); - res = RES_IO_ERR; + /* Create enclosures */ + tmp_res = init_enclosures(stardis); + if(tmp_res != RES_OK && is_for_compute) { + res = tmp_res; goto error; } - - /* loop on boundaries */ - while (read_line(&line, input)){ - char* stl_filename = NULL; - struct description desc; - - init_description(&desc); - res = parse_boundary_line(line, &stl_filename, &desc); - if (res != RES_OK) goto error; - - /* Deduplicate media: find if the same description already exists - * (including name) */ - p_desc = htable_descriptions_find(&descriptions, &desc); - if (!p_desc) { - sa_push(stardis->descriptions, desc); - desc_id = sz++; - ASSERT(sz == sa_size(stardis->descriptions)); - /* Register new boundary description */ - res = htable_descriptions_set(&descriptions, &desc, &desc_id); - if (res != RES_OK) goto error; - } else { - fprintf(stderr, "Duplicate boundary description found: deduplicated.\n"); - desc_id = *p_desc; + ERR(senc3d_scene_get_overlapping_triangles_count(stardis->senc3d_scn, &ocount)); + if(ocount) { + logger_print(stardis->logger, (is_for_compute ? LOG_ERROR : LOG_WARNING), + "Scene contains %u overlapping triangles.\n", + ocount); + if(is_for_compute) { + res = RES_BAD_ARG; + goto error; } - - res = read_stl(desc_id, CONTENT_CONNECTION, stl_filename, &vertex2id, - &triangle2id, stardis); - if (stl_filename) free(stl_filename); - if (res != RES_OK) goto error; } - fclose(input); - input = NULL; - /* Process vtk file describing boundary compute */ - if (bound_filename) { - res = read_stl(0, CONTENT_BOUNDARY, bound_filename, &vertex2id, - &triangle2id, stardis); - if (res != RES_OK) goto error; + /* Create solids and log model information */ + for(i = 0; i < darray_descriptions_size_get(&stardis->descriptions); i++) { + struct description* desc = + darray_descriptions_data_get(&stardis->descriptions) + i; + if(desc->type == DESC_MAT_SOLID) { + tmp_res = check_delta_and_create_solid(stardis, desc); + if(tmp_res != RES_OK && is_for_compute) { + res = tmp_res; + goto error; + } + } + ERR(str_print_description(&str, i, desc)); + logger_print(stardis->logger, LOG_OUTPUT, "%s\n", str_cget(&str)); } -exit: - if(input) fclose(input); - if (tables_initialised) { - htable_descriptions_release(&descriptions); - htable_vertex_release(&vertex2id); - htable_triangle_release(&triangle2id); + if(is_for_compute) { + for(i = 0; i < tcount; ++i) { + ERR(create_intface(stardis, i, &htable_interfaces)); + } + if(args->paths_filename) { + ERR(str_set(&stardis->paths_filename, args->paths_filename)); + } + if(args->bin_green_filename) { + ERR(str_set(&stardis->bin_green_filename, args->bin_green_filename)); + } + if(args->end_paths_filename) { + ERR(str_set(&stardis->end_paths_filename, args->end_paths_filename)); + } } - sa_release(line); - return res; -error: - goto exit; -} - -static res_T -parse_camera - (char* cam_param, struct camera* cam) -{ - char** line = NULL; - char** opt = NULL; - res_T res = RES_OK; - line = split_line(cam_param,':'); - - if (line) - { - int i = 0; - for (i = 0; *(line + i); i++) - { - size_t len = 0; - opt = split_line(line[i],'='); - if (strcmp(opt[0], "t") == 0) { - res = cstr_to_double(opt[1], &cam->u.time); - if (res != RES_OK) goto error; - } else if (strcmp(opt[0],"fov")==0){ - res = cstr_to_double(opt[1], &cam->fov); - if (res != RES_OK) goto error; - } else if (strcmp(opt[0],"up")==0) { - res = cstr_to_list_double(opt[1], ',', cam->up, &len, 3); - if (res != RES_OK || len != 3) goto error; - } else if (strcmp(opt[0],"tgt")==0) { - res = cstr_to_list_double(opt[1], ',', cam->tgt, &len, 3); - if (res != RES_OK || len != 3) goto error; - } else if (strcmp(opt[0],"pos")==0) { - res = cstr_to_list_double(opt[1], ',', cam->pos, &len, 3); - if (res != RES_OK || len != 3) goto error; - } else if (strcmp(opt[0],"img")==0) { - res = cstr_to_list_uint(opt[1], 'x', cam->img, &len, 2); - if (res != RES_OK || len != 2) goto error; - } else if (strcmp(opt[0],"spp")==0) { - res = cstr_to_uint(opt[1], &cam->spp); - if (res != RES_OK) goto error; - } + /* If computation is on a volume, check medium is known */ + if(args->mode & MODE_MEDIUM_COMPUTE) { + if(!find_medium_by_name(stardis, &stardis->solve_name, NULL)) { + logger_print(stardis->logger, (is_for_compute ? LOG_ERROR : LOG_WARNING), + "Cannot find medium '%s'\n", str_cget(&stardis->solve_name)); + res = RES_BAD_ARG; + goto error; + } + } + + if(is_for_compute) { + struct sdis_scene_create_args scn_args = SDIS_SCENE_CREATE_ARGS_DEFAULT; + ASSERT(darray_interface_ptrs_size_get(&stardis->geometry.interf_bytrg) + == tcount); + /* Can release enclosures as they are no longer needed if compute */ + SENC3D(scene_ref_put(stardis->senc3d_scn)); + stardis->senc3d_scn = NULL; + scn_args.get_indices = sg3d_sdisXd_geometry_get_indices; + scn_args.get_interface = sg3d_sdisXd_geometry_get_interface; + scn_args.get_position = sg3d_sdisXd_geometry_get_position; + scn_args.nprimitives = tcount; + scn_args.nvertices = vcount; + scn_args.fp_to_meter = stardis->scale_factor; + scn_args.trad = stardis->ambient_temp; + scn_args.tref = stardis->ref_temp; + scn_args.context = &create_context; + res = sdis_scene_create(stardis->dev, &scn_args, &stardis->sdis_scn); + if(res != RES_OK) { + logger_print(stardis->logger, LOG_ERROR, + "Cannot create the stardis solver scene.\n"); + goto error; } } exit: - FREE_AARRAY(line) - FREE_AARRAY(opt) - + str_release(&str); + htable_intface_release(&htable_interfaces); return res; error: + stardis_release(stardis); goto exit; } -/******************************************************************************* - * - ******************************************************************************/ +void +stardis_release + (struct stardis* stardis) +{ + size_t i; + + ASSERT(stardis); + + if(stardis->dev) SDIS(device_ref_put(stardis->dev)); + if(stardis->sdis_scn) SDIS(scene_ref_put(stardis->sdis_scn)); + if(stardis->senc3d_scn) SENC3D(scene_ref_put(stardis->senc3d_scn)); + str_release(&stardis->solve_name); + str_release(&stardis->paths_filename); + str_release(&stardis->bin_green_filename); + str_release(&stardis->end_paths_filename); + str_release(&stardis->chunks_prefix); + darray_descriptions_release(&stardis->descriptions); + release_geometry(&stardis->geometry); + darray_size_t_release(&stardis->compute_surface.primitives); + darray_sides_release(&stardis->compute_surface.sides); + darray_uint_release(&stardis->compute_surface.err_triangles); + FOR_EACH(i, 0, darray_media_ptr_size_get(&stardis->media)) { + if(darray_media_ptr_data_get(&stardis->media)[i]) + SDIS(medium_ref_put(darray_media_ptr_data_get(&stardis->media)[i])); + } + darray_media_ptr_release(&stardis->media); +} res_T -stardis_init - (const struct args* args, - struct stardis* stardis) +init_enclosures + (struct stardis* stardis) { res_T res = RES_OK; - - res = mem_init_proxy_allocator(&stardis->allocator, - &mem_default_allocator); - if (res != RES_OK) goto error; - - res = geometry_analyse(args->medium_filename, - args->bc_filename, args->solve_boundary_filename, stardis); - if (res != RES_OK) goto error; - - stardis->N = args->N; - stardis->nthreads = args->nthreads; - stardis->probe[0] = args->u.probe[0]; - stardis->probe[1] = args->u.probe[1]; - stardis->probe[2] = args->u.probe[2]; - stardis->probe[3] = args->u.probe[3]; - stardis->scale_factor = args->scale_factor; - stardis->radiative_temp[0] = args->radiative_temp[0]; - stardis->radiative_temp[1] = args->radiative_temp[1]; - stardis->dump_paths = SDIS_HEAT_PATH_NONE; - if (args->dump_paths & DUMP_ERROR) - stardis->dump_paths |= SDIS_HEAT_PATH_FAILED; - if (args->dump_paths & DUMP_SUCCESS) - stardis->dump_paths |= SDIS_HEAT_PATH_SUCCEED; - - if (args->mode & IR_COMPUTE){ - res = parse_camera(args->camera, &stardis->camera); - if (res != RES_OK) goto error; - } - else if (args->mode & MEDIUM_COMPUTE) { - strcpy(stardis->solve_name, args->medium_name); - } - else if (args->mode & BOUNDARY_COMPUTE) { - strcpy(stardis->solve_name, args->solve_boundary_filename); - } - + unsigned tsz, vsz; + struct senc3d_device* senc_dev = NULL; + + ERR(sg3d_geometry_get_unique_triangles_count(stardis->geometry.sg3d, &tsz)); + ERR(sg3d_geometry_get_unique_vertices_count(stardis->geometry.sg3d, &vsz)); + ERR(senc3d_device_create(stardis->logger, stardis->allocator, + stardis->nthreads, stardis->verbose, &senc_dev)); + stardis->undefined_medium_behind_boundary_id + = allocate_stardis_medium_id(stardis); + ERR(senc3d_scene_create(senc_dev, + SENC3D_CONVENTION_NORMAL_BACK | SENC3D_CONVENTION_NORMAL_OUTSIDE, + tsz, sg3d_sencXd_geometry_get_indices, sg3d_sencXd_geometry_get_media, + vsz, sg3d_sencXd_geometry_get_position, stardis->geometry.sg3d, + &stardis->senc3d_scn)); exit: + if(senc_dev) senc3d_device_ref_put(senc_dev); return res; error: goto exit; } -void -stardis_release(struct stardis* stardis) -{ - size_t i = 0; +#define COUNT_SIDE(Rank) {\ + if(properties[(Rank)] == SG3D_UNSPECIFIED_PROPERTY) undef_count++;\ + else {\ + ASSERT(properties[(Rank)] < darray_descriptions_size_get(&stardis->descriptions));\ + ASSERT(descs[properties[(Rank)]].type == DESC_MAT_SOLID\ + || descs[properties[(Rank)]].type == DESC_MAT_FLUID);\ + if(descs[properties[(Rank)]].type == DESC_MAT_SOLID) solid_count++;\ + else fluid_count++;\ + }\ +} - for (i=0; i<sa_size(stardis->descriptions); ++i) { - struct description* desc = &stardis->descriptions[i]; - switch (desc->type) { - case DESC_MAT_SOLID: - free(desc->d.solid.power); - free(desc->d.solid.Tinit); - free(desc->d.solid.Temp); - break; - case DESC_MAT_FLUID: - free(desc->d.fluid.Tinit); - free(desc->d.fluid.Temp); +res_T +validate_properties + (const unsigned itri, + const unsigned properties[SG3D_PROP_TYPES_COUNT__], + void* context, + int* properties_conflict_status) +{ + res_T res = RES_OK; + struct stardis* stardis = context; + unsigned undef_count, solid_count, fluid_count, intface_count; + const struct description* descs; + const struct description* intface = NULL; + + (void)itri; + ASSERT(stardis && properties_conflict_status); + descs = darray_descriptions_cdata_get(&stardis->descriptions); + *properties_conflict_status = NO_PROPERTY_CONFLICT; + undef_count = solid_count = fluid_count = intface_count = 0; + + COUNT_SIDE(SG3D_FRONT); + COUNT_SIDE(SG3D_BACK); + if(properties[SG3D_INTFACE] == SG3D_UNSPECIFIED_PROPERTY) + undef_count++; + else intface_count++; + + ASSERT(solid_count <= 2 && fluid_count <= 2 && intface_count <= 1); + ASSERT(undef_count + solid_count + fluid_count + intface_count == 3); + if(intface_count) { + ASSERT(properties[SG3D_INTFACE] + < darray_descriptions_size_get(&stardis->descriptions)); + intface = descs + properties[SG3D_INTFACE]; + switch (intface->type) { + case DESC_BOUND_H_FOR_FLUID: + if(!(solid_count == 0 && fluid_count == 1)) { + if(solid_count + fluid_count == 2) + *properties_conflict_status = BOUND_H_FOR_FLUID_BETWEEN_2_DEFS; + else if(solid_count + fluid_count == 0) + *properties_conflict_status = BOUND_H_FOR_FLUID_BETWEEN_2_UNDEFS; + else if(solid_count == 1) + *properties_conflict_status = BOUND_H_FOR_FLUID_ENCLOSING_SOLID; + else FATAL("error:" STR(__FILE__) ":" STR(__LINE__)"\n"); + goto end; + } break; case DESC_BOUND_H_FOR_SOLID: - case DESC_BOUND_H_FOR_FLUID: - free(desc->d.h_boundary.T); + if(!(solid_count == 1 && fluid_count == 0)) { + if(solid_count + fluid_count == 2) + *properties_conflict_status = BOUND_H_FOR_SOLID_BETWEEN_2_DEFS; + else if(solid_count + fluid_count == 0) + *properties_conflict_status = BOUND_H_FOR_SOLID_BETWEEN_2_UNDEFS; + else if(fluid_count == 1) + *properties_conflict_status = BOUND_H_FOR_SOLID_ENCLOSING_FLUID; + else FATAL("error:" STR(__FILE__) ":" STR(__LINE__)"\n"); + goto end; + } break; - case DESC_BOUND_T_FOR_SOLID: case DESC_BOUND_T_FOR_FLUID: - free(desc->d.t_boundary.T); - te_free(desc->d.t_boundary.te_temperature); + if(!(solid_count == 0 && fluid_count == 1)) { + if(solid_count + fluid_count == 2) + *properties_conflict_status = BOUND_T_FOR_FLUID_BETWEEN_2_DEFS; + else if(solid_count + fluid_count == 0) + *properties_conflict_status = BOUND_T_FOR_FLUID_BETWEEN_2_UNDEFS; + else if(solid_count == 1) + *properties_conflict_status = BOUND_T_FOR_FLUID_ENCLOSING_SOLID; + else FATAL("error:" STR(__FILE__) ":" STR(__LINE__)"\n"); + goto end; + } + break; + case DESC_BOUND_T_FOR_SOLID: + if(!(solid_count == 1 && fluid_count == 0)) { + if(solid_count + fluid_count == 2) + *properties_conflict_status = BOUND_T_FOR_SOLID_BETWEEN_2_DEFS; + else if(solid_count + fluid_count == 0) + *properties_conflict_status = BOUND_T_FOR_SOLID_BETWEEN_2_UNDEFS; + else if(fluid_count == 1) + *properties_conflict_status = BOUND_T_FOR_SOLID_ENCLOSING_FLUID; + else FATAL("error:" STR(__FILE__) ":" STR(__LINE__)"\n"); + goto end; + } break; case DESC_BOUND_F_FOR_SOLID: - free(desc->d.f_boundary.flux); - te_free(desc->d.f_boundary.te_flux); + if(!(solid_count == 1 && fluid_count == 0)) { + if(solid_count + fluid_count == 2) + *properties_conflict_status = BOUND_F_FOR_SOLID_BETWEEN_2_DEFS; + else if(solid_count + fluid_count == 0) + *properties_conflict_status = BOUND_F_FOR_SOLID_BETWEEN_2_UNDEFS; + else if(fluid_count == 1) + *properties_conflict_status = BOUND_F_FOR_SOLID_ENCLOSING_FLUID; + else FATAL("error:" STR(__FILE__) ":" STR(__LINE__)"\n"); + goto end; + } break; - default: FATAL("Invalid type.\n"); + case DESC_SOLID_FLUID_CONNECT: + if(solid_count != 1 || fluid_count != 1) { + if(solid_count == 2) + *properties_conflict_status = SFCONNECT_BETWEEN_2_SOLIDS; + else if(fluid_count == 2) + *properties_conflict_status = SFCONNECT_BETWEEN_2_FLUIDS; + else if(solid_count + fluid_count == 1) + *properties_conflict_status = SFCONNECT_USED_AS_BOUNDARY; + else if(solid_count + fluid_count == 0) + *properties_conflict_status = SFCONNECT_BETWEEN_2_UNDEFS; + else FATAL("error:" STR(__FILE__) ":" STR(__LINE__)"\n"); + goto end; + } + break; + default: + FATAL("error:" STR(__FILE__) ":" STR(__LINE__)": Invalid type.\n"); } - } - sa_release(stardis->descriptions); - release_geometry(&stardis->geometry); - sa_release(stardis->boundary.primitives); - sa_release(stardis->boundary.sides); -} - -/* TODO: rewrite dump! */ -res_T -dump_vtk - (FILE* output, - const struct geometry* geometry) -{ - res_T res = RES_OK; - unsigned i; - struct vertex* vtx = geometry->vertex; - struct triangle* tri = geometry->triangle; - - if (!output){ - fprintf(stderr, "Invalid output file to dump vtk.\n"); - res = RES_BAD_ARG; - goto error; - } - - fprintf(output,"# vtk DataFile Version 2.0\nvtk output\nASCII\nDATASET POLYDATA\n"); - fprintf(output,"POINTS %i float\n\n", (int)sa_size(vtx)); - for (i=0; i<sa_size(vtx); ++i){ - fprintf(output,"%f %f %f\n",SPLIT3(vtx[i].xyz)); - } - fprintf(output,"\nPOLYGONS %i %i\n", (int)sa_size(tri), (int)(4*sa_size(tri))); - for (i=0; i<sa_size(tri); ++i){ - fprintf(output,"3 %i %i %i\n",SPLIT3(tri[i].indices.data)); - } - fprintf(output,"\nCELL_DATA %i \n", (int)sa_size(tri)); - fprintf(output,"SCALARS front_description float 1\n"); - fprintf(output,"LOOKUP_TABLE default\n"); - for (i=0; i<sa_size(tri); ++i) { - fprintf(output,"%i\n",tri[i].front_description); - } - fprintf(output,"SCALARS back_description float 1\n"); - fprintf(output,"LOOKUP_TABLE default\n"); - for (i=0; i<sa_size(tri); ++i) { - fprintf(output,"%i\n",tri[i].back_description); + } else { + /* No interface defined */ + ASSERT(intface_count == 0 && undef_count >= 1); + if(undef_count == 3) { + *properties_conflict_status = TRG_WITH_NO_PROPERTY; + goto end; + } + if(fluid_count == 2) { + *properties_conflict_status = NO_CONNECTION_BETWEEN_2_FLUIDS; + goto end; + } + if(undef_count == 2) { + ASSERT(fluid_count + solid_count == 1); + if(fluid_count) + *properties_conflict_status = NO_BOUND_BETWEEN_FLUID_AND_UNDEF; + else *properties_conflict_status = NO_BOUND_BETWEEN_SOLID_AND_UNDEF; + goto end; + } + if(undef_count == 1 && solid_count == 1 && fluid_count == 1) { + *properties_conflict_status = NO_CONNECTION_BETWEEN_SOLID_AND_FLUID; + goto end; + } + /* Undef interface between solids is OK */ + CHK(solid_count == 2); } -exit: +end: return res; -error: - goto exit; } + +#undef COUNT_SIDE diff --git a/src/stardis-app.h b/src/stardis-app.h @@ -1,24 +1,100 @@ -/* Copyright (C) 2018 |Meso|Star> (contact@meso-star.com)*/ +/* Copyright (C) 2018-2020 |Meso|Star> (contact@meso-star.com) + * + * This program 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. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef STARDIS_APP_H #define STARDIS_APP_H +#include "stardis-parsing.h" +#include "stardis-default.h" +#include "stardis-solid.h" +#include "stardis-fluid.h" + #include <star/sstl.h> +#include <star/sg3d.h> + #include <rsys/rsys.h> #include <rsys/float3.h> -#include <rsys/stretchy_array.h> -#include <rsys/hash_table.h> +#include <rsys/double2.h> +#include <rsys/double3.h> +#include <rsys/dynamic_array_size_t.h> +#include <rsys/dynamic_array.h> +#include <rsys/str.h> + #include <sdis.h> -#include <tinyexpr.h> -#include "args.h" - -#define FREE_AARRAY(ARRAY) if (ARRAY) {\ - int i = 0; \ - for (i=0; *(ARRAY+i);i++){\ - free(ARRAY[i]);\ - }\ - free(ARRAY);\ -} + +#include <limits.h> +#include <stdarg.h> + +/* Forward declarations */ +struct logger; +struct mem_allocator; +struct sdis_medium; + +/* Utility macros */ +#define ERR(Expr) if((res = (Expr)) != RES_OK) goto error; else (void)0 + +#define STR_APPEND_PRINTF(Accum, Fmt, Args) \ +{ \ + struct str tmp; \ + str_init((Accum)->allocator, &tmp); \ + res = str_printf(&tmp, Fmt COMMA_##Args LIST_##Args); \ + if(res == RES_OK) res = str_append((Accum), str_cget(&tmp)); \ + str_release(&tmp); \ + if(res != RES_OK) goto error; \ +} (void)0 + + +#define VFATAL(Fmt, Args) \ + { \ + fprintf(stderr, Fmt COMMA_##Args LIST_##Args); \ + ASSERT(0); \ + abort(); \ + } (void)0 + + +#define DELTA_AUTO INF /* Placeholder until actual value is substituted */ +#define UNKNOWN_MEDIUM_TEMPERATURE -1 /* Unknown for stadis solver is -1 */ + +enum properties_conflict_t { + NO_PROPERTY_CONFLICT, + BOUND_H_FOR_FLUID_BETWEEN_2_DEFS, + BOUND_H_FOR_FLUID_BETWEEN_2_UNDEFS, + BOUND_H_FOR_FLUID_ENCLOSING_SOLID, + BOUND_H_FOR_SOLID_BETWEEN_2_DEFS, + BOUND_H_FOR_SOLID_BETWEEN_2_UNDEFS, + BOUND_H_FOR_SOLID_ENCLOSING_FLUID, + BOUND_T_FOR_FLUID_BETWEEN_2_DEFS, + BOUND_T_FOR_FLUID_BETWEEN_2_UNDEFS, + BOUND_T_FOR_FLUID_ENCLOSING_SOLID, + BOUND_T_FOR_SOLID_BETWEEN_2_DEFS, + BOUND_T_FOR_SOLID_BETWEEN_2_UNDEFS, + BOUND_T_FOR_SOLID_ENCLOSING_FLUID, + BOUND_F_FOR_SOLID_BETWEEN_2_DEFS, + BOUND_F_FOR_SOLID_BETWEEN_2_UNDEFS, + BOUND_F_FOR_SOLID_ENCLOSING_FLUID, + SFCONNECT_BETWEEN_2_SOLIDS, + SFCONNECT_BETWEEN_2_FLUIDS, + SFCONNECT_USED_AS_BOUNDARY, + SFCONNECT_BETWEEN_2_UNDEFS, + NO_CONNECTION_BETWEEN_2_FLUIDS, + NO_CONNECTION_BETWEEN_SOLID_AND_FLUID, + NO_BOUND_BETWEEN_FLUID_AND_UNDEF, + NO_BOUND_BETWEEN_SOLID_AND_UNDEF, + TRG_WITH_NO_PROPERTY, + PROPERTIES_CONFLICT_COUNT__ +}; /* Different types of descriptions */ enum description_type { @@ -34,841 +110,316 @@ enum description_type { DESC_OUTSIDE }; -struct vertex{ - float xyz[3]; -}; -#define NULL_VERTEX__ {{0.0 ,0.0 ,0.0}} -static const struct vertex NULL_VERTEX = NULL_VERTEX__; - -static INLINE char -eq_vertex(const struct vertex* a, const struct vertex* b) -{ - return (char)f3_eq(a->xyz, b->xyz); -} - -/* Declare the hash table that map a vertex to its index */ -#define HTABLE_NAME vertex -#define HTABLE_DATA unsigned -#define HTABLE_KEY struct vertex -#define HTABLE_KEY_FUNCTOR_EQ eq_vertex -#include <rsys/hash_table.h> - -struct unsigned3 { unsigned data[3]; }; -struct triangle { - struct unsigned3 indices; - unsigned front_description; - unsigned back_description; - unsigned connection_description; +#define DESC_IS_H(D) \ + ((D) == DESC_BOUND_H_FOR_SOLID || (D) == DESC_BOUND_H_FOR_FLUID) +#define DESC_IS_T(D) \ + ((D) == DESC_BOUND_T_FOR_SOLID || (D) == DESC_BOUND_T_FOR_FLUID) +#define DESC_IS_F(D) \ + ((D) == DESC_BOUND_F_FOR_SOLID) +#define DESC_IS_MEDIUM(D) \ + ((D) == DESC_MAT_SOLID || (D) == DESC_MAT_FLUID) +#define DESC_IS_BOUNDARY(D) \ + (DESC_IS_H(D) || DESC_IS_T(D) || DESC_IS_F(D)) + +#define DARRAY_NAME interface_ptrs +#define DARRAY_DATA struct sdis_interface* +#include <rsys/dynamic_array.h> + +struct dummies { + struct sdis_medium* dummy_fluid; + unsigned dummy_fluid_id; + struct sdis_medium* dummy_solid; + unsigned dummy_solid_id; }; -#define NULL_TRIANGLE__ {{{0, 0, 0}}, UINT_MAX, UINT_MAX, UINT_MAX } -static const struct triangle NULL_TRIANGLE = NULL_TRIANGLE__; - -static INLINE void -print_trg_as_obj - (FILE* stream, - const struct vertex* vertices, - const unsigned* indices) -{ - ASSERT(stream && vertices && indices); - fprintf(stream, "v %.8f %.8f %.8f\nv %.8f %.8f %.8f\nv %.8f %.8f %.8f\nf 1 2 3\n", - SPLIT3(vertices[indices[0]].xyz), - SPLIT3(vertices[indices[1]].xyz), - SPLIT3(vertices[indices[2]].xyz)); -} - -static INLINE char -eq_indices(const struct unsigned3* a, const struct unsigned3* b) -{ - return a->data[0] == b->data[0] - && a->data[1] == b->data[1] - && a->data[2] == b->data[2]; +#define DUMMIES_NULL__ {\ + NULL, UINT_MAX, NULL, UINT_MAX\ } -static INLINE res_T -cp_indices(struct unsigned3* dst, const struct unsigned3* src) +static FINLINE void +init_media_ptr + (struct mem_allocator* alloc, + struct sdis_medium** data) { - int i; - FOR_EACH(i, 0, 3) dst->data[i] = src->data[i]; - return RES_OK; + ASSERT(data); (void)alloc; + *data = NULL; } -/* Declare the hash table that maps a triangle id to its index */ -#define HTABLE_NAME triangle -#define HTABLE_DATA unsigned -#define HTABLE_KEY struct unsigned3 -#define HTABLE_KEY_FUNCTOR_EQ eq_indices -#define HTABLE_KEY_FUNCTOR_COPY cp_indices -#include <rsys/hash_table.h> +#define DARRAY_NAME media_ptr +#define DARRAY_DATA struct sdis_medium* +#define DARRAY_FUNCTOR_INIT init_media_ptr +#include <rsys/dynamic_array.h> struct geometry { - struct vertex* vertex; - struct triangle* triangle; - struct sdis_interface** interf_bytrg; - struct sdis_interface** interfaces; + struct sg3d_geometry* sg3d; + struct darray_interface_ptrs interf_bytrg; + struct darray_interface_ptrs interfaces; }; -#define NULL_GEOMETRY__ {NULL, NULL, NULL, NULL } -static const struct geometry NULL_GEOMETRY = NULL_GEOMETRY__; static INLINE void release_geometry(struct geometry* geom) { size_t i; - for (i = 0; i < sa_size(geom->interfaces); ++i) - SDIS(interface_ref_put(geom->interfaces[i])); - sa_release(geom->vertex); geom->vertex = NULL; - sa_release(geom->triangle); geom->triangle = NULL; - sa_release(geom->interfaces); geom->interfaces = NULL; - sa_release(geom->interf_bytrg); geom->interf_bytrg = NULL; + struct sdis_interface + ** intf = darray_interface_ptrs_data_get(&geom->interfaces); + if(geom->sg3d) SG3D(geometry_ref_put(geom->sg3d)); + for(i = 0; i < darray_interface_ptrs_size_get(&geom->interfaces); ++i) + SDIS(interface_ref_put(intf[i])); + darray_interface_ptrs_release(&geom->interfaces); + darray_interface_ptrs_release(&geom->interf_bytrg); } -struct mat_fluid { - char name[32]; - unsigned fluid_id; - double rho; - double cp; - char* Tinit; - char* Temp; -}; -#define NULL_FLUID__ { "", UINT_MAX, 0, 0, NULL, NULL } -static const struct mat_fluid NULL_FLUID = NULL_FLUID__; - -static void -print_fluid(FILE* stream, const struct mat_fluid* f) +static INLINE res_T +init_geometry + (struct logger* logger, + struct mem_allocator* allocator, + const int verbose, + struct geometry* geom) { - ASSERT(stream && f); - fprintf(stream, "Fluid '%s': cp=%g rho=%g", - f->name, f->cp, f->rho); - if (f->Tinit) fprintf(stream, " Tinit='%s'", f->Tinit); - if (f->Temp) fprintf(stream, " Temp='%s'", f->Temp); - fprintf(stream, "\n"); + res_T res = RES_OK; + struct sg3d_device* sg3d_dev = NULL; + + ASSERT(allocator && geom); + + geom->sg3d = NULL; + darray_interface_ptrs_init(allocator, &geom->interfaces); + darray_interface_ptrs_init(allocator, &geom->interf_bytrg); + ERR(sg3d_device_create(logger, allocator, verbose, &sg3d_dev)); + ERR(sg3d_geometry_create(sg3d_dev, &geom->sg3d)); + +exit: + if(sg3d_dev) SG3D(device_ref_put(sg3d_dev)); + return res; +error: + release_geometry(geom); + goto exit; } -static char -eq_fluid(const struct mat_fluid* a, const struct mat_fluid* b) -{ - if (strcmp(a->name, b->name) - || a->fluid_id != b->fluid_id - || a->rho != b->rho - || a->cp != b->cp - || strcmp(a->Tinit, b->Tinit) - || strcmp(a->Temp, b->Temp)) - return 0; - return 1; -} +/******************************************************************************/ -static size_t -hash_fluid(const struct mat_fluid* key) -{ -#ifdef ARCH_32BITS - /* 32-bits Fowler/Noll/Vo hash function */ - const uint32_t FNV32_PRIME = - (uint32_t) (((uint32_t) 1 << 24) + ((uint32_t) 1 << 8) + 0x93); - const uint32_t OFFSET32_BASIS = 2166136261u; - uint32_t hash = OFFSET32_BASIS; - size_t i; - ASSERT(key); - FOR_EACH(i, 0, sizeof(key->name)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->name)[i]); - hash = hash * FNV32_PRIME; - } - FOR_EACH(i, 0, sizeof(key->fluid_id)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->fluid_id)[i]); - hash = hash * FNV32_PRIME; - } - FOR_EACH(i, 0, sizeof(key->rho)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->rho)[i]); - hash = hash * FNV32_PRIME; - } - FOR_EACH(i, 0, sizeof(key->cp)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->cp)[i]); - hash = hash * FNV32_PRIME; - } - hash = hash ^ (uint32_t)((unsigned char)(key->Tinit == 0)); - hash = hash * FNV32_PRIME; - FOR_EACH(i, 0, key->Tinit ? strlen(key->Tinit) * sizeof(*key->Tinit) : 0) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)key->Tinit)[i]); - hash = hash * FNV32_PRIME; - } - hash = hash ^ (uint32_t)((unsigned char)(key->Temp == 0)); - hash = hash * FNV32_PRIME; - FOR_EACH(i, 0, key->Temp ? strlen(key->Temp) * sizeof(*key->Temp) : 0) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)key->Temp)[i]); - hash = hash * FNV32_PRIME; - } - return hash; -#elif defined(ARCH_64BITS) - /* 64-bits Fowler/Noll/Vo hash function */ - const uint64_t FNV64_PRIME = - (uint64_t) (((uint64_t) 1 << 40) + ((uint64_t) 1 << 8) + 0xB3); - const uint64_t OFFSET64_BASIS = (uint64_t) 14695981039346656037u; - uint64_t hash = OFFSET64_BASIS; - size_t i; - ASSERT(key); - FOR_EACH(i, 0, sizeof(key->name)) { - hash = hash ^ (uint64_t)((unsigned char)((const char*)&key->name)[i]); - hash = hash * FNV64_PRIME; - } - FOR_EACH(i, 0, sizeof(key->fluid_id)) { - hash = hash ^ (uint64_t)((unsigned char)((const char*)&key->fluid_id)[i]); - hash = hash * FNV64_PRIME; - } - FOR_EACH(i, 0, sizeof(key->rho)) { - hash = hash ^ (uint64_t) ((unsigned char)((const char*)&key->rho)[i]); - hash = hash * FNV64_PRIME; - } - FOR_EACH(i, 0, sizeof(key->cp)) { - hash = hash ^ (uint64_t)((unsigned char)((const char*)&key->cp)[i]); - hash = hash * FNV64_PRIME; - } - hash = hash ^ (uint64_t)((unsigned char)(key->Tinit == 0)); - hash = hash * FNV64_PRIME; - FOR_EACH(i, 0, key->Tinit ? strlen(key->Tinit)*sizeof(*key->Tinit) : 0) { - hash = hash ^ (uint64_t)((unsigned char) ((const char*)key->Tinit)[i]); - hash = hash * FNV64_PRIME; - } - hash = hash ^ (uint64_t)((unsigned char)(key->Temp == 0)); - hash = hash * FNV64_PRIME; - FOR_EACH(i, 0, key->Temp ? strlen(key->Temp) * sizeof(*key->Temp) : 0) { - hash = hash ^ (uint64_t)((unsigned char)((const char*)key->Temp)[i]); - hash = hash * FNV64_PRIME; - } - return hash; -#else -#error "Unexpected architecture" -#endif -} - -struct mat_solid { - char name[32]; - unsigned solid_id; - double lambda; - double rho; - double cp; - double delta; - char* Tinit; - char* Temp; - char* power; - int has_power; +struct h_boundary { + struct str name; + double emissivity; + double specular_fraction; + double hc; + double imposed_temperature; + unsigned mat_id; }; -#define NULL_SOLID__ { "", UINT_MAX, 0, 0, 0, 0, NULL, NULL, NULL, 0} -static const struct mat_solid NULL_SOLID = NULL_SOLID__; -static void -print_solid(FILE* stream, const struct mat_solid* s) +static FINLINE void +init_h(struct mem_allocator* allocator, struct h_boundary* dst) { - ASSERT(stream && s); - fprintf(stream, - "Solid '%s': lambda=%g cp=%g rho=%g delta=%g Power='%s'", - s->name, s->lambda, s->cp, s->rho, s->delta, - (s->has_power ? s->power : "0")); - if (s->Tinit) fprintf(stream, " Tinit='%s'", s->Tinit); - if (s->Temp) fprintf(stream, " Temp='%s'", s->Temp); - fprintf(stream, "\n"); + str_init(allocator, &dst->name); + dst->emissivity = 0; + dst->specular_fraction = 0; + dst->hc = 0; + dst->imposed_temperature = -1; + dst->mat_id = UINT_MAX; } -static char -eq_solid(const struct mat_solid* a, const struct mat_solid* b) +static FINLINE void +release_h_boundary(struct h_boundary* bound) { - if (strcmp(a->name, b->name) - || a->solid_id != b->solid_id - || a->lambda != b->lambda - || a->rho != b->rho - || a->cp != b->cp - || a->delta != b->delta - || strcmp(a->Tinit, b->Tinit) - || strcmp(a->Temp, b->Temp) - || a->has_power != b->has_power) - return 0; - if (a->has_power && a->power != b->power) return 0; - return 1; -} - -static size_t -hash_solid(const struct mat_solid* key) -{ -#ifdef ARCH_32BITS - /* 32-bits Fowler/Noll/Vo hash function */ - const uint32_t FNV32_PRIME = - (uint32_t) (((uint32_t) 1 << 24) + ((uint32_t) 1 << 8) + 0x93); - const uint32_t OFFSET32_BASIS = 2166136261u; - uint32_t hash = OFFSET32_BASIS; - size_t i; - ASSERT(key); - FOR_EACH(i, 0, sizeof(key->name)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->name)[i]); - hash = hash * FNV32_PRIME; - } - FOR_EACH(i, 0, sizeof(key->solid_id)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->solid_id)[i]); - hash = hash * FNV32_PRIME; - } - FOR_EACH(i, 0, sizeof(key->lambda)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->lambda)[i]); - hash = hash * FNV32_PRIME; - } - FOR_EACH(i, 0, sizeof(key->rho)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->rho)[i]); - hash = hash * FNV32_PRIME; - } - FOR_EACH(i, 0, sizeof(key->cp)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->cp)[i]); - hash = hash * FNV32_PRIME; - } - FOR_EACH(i, 0, sizeof(key->delta)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->delta)[i]); - hash = hash * FNV32_PRIME; - } - hash = hash ^ (uint32_t)((unsigned char)(key->Tinit == 0)); - hash = hash * FNV32_PRIME; - FOR_EACH(i, 0, key->Tinit ? strlen(key->Tinit) * sizeof(*key->Tinit) : 0) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)key->Tinit)[i]); - hash = hash * FNV32_PRIME; - } - hash = hash ^ (uint32_t)((unsigned char)(key->Temp == 0)); - hash = hash * FNV32_PRIME; - FOR_EACH(i, 0, key->Temp ? strlen(key->Temp) * sizeof(*key->Temp) : 0) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)key->Temp)[i]); - hash = hash * FNV32_PRIME; - } - FOR_EACH(i, 0, sizeof(key->has_power)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->has_power)[i]); - hash = hash * FNV32_PRIME; - } - if (!key->has_power) return hash; - FOR_EACH(i, 0, strlen(key->Tinit) * sizeof(*key->power)) { - hash = hash ^ (uint32_t) ((unsigned char) ((const char*) key->power)[i]); - hash = hash * FNV32_PRIME; - } - return hash; -#elif defined(ARCH_64BITS) - /* 64-bits Fowler/Noll/Vo hash function */ - const uint64_t FNV64_PRIME = - (uint64_t) (((uint64_t) 1 << 40) + ((uint64_t) 1 << 8) + 0xB3); - const uint64_t OFFSET64_BASIS = (uint64_t) 14695981039346656037u; - uint64_t hash = OFFSET64_BASIS; - size_t i; - ASSERT(key); - FOR_EACH(i, 0, sizeof(key->name)) { - hash = hash ^ (uint64_t)((unsigned char)((const char*)&key->name)[i]); - hash = hash * FNV64_PRIME; - } - FOR_EACH(i, 0, sizeof(key->solid_id)) { - hash = hash ^ (uint64_t)((unsigned char)((const char*)&key->solid_id)[i]); - hash = hash * FNV64_PRIME; - } - FOR_EACH(i, 0, sizeof(key->lambda)) { - hash = hash ^ (uint64_t)((unsigned char)((const char*)&key->lambda)[i]); - hash = hash * FNV64_PRIME; - } - FOR_EACH(i, 0, sizeof(key->rho)) { - hash = hash ^ (uint64_t)((unsigned char)((const char*)&key->rho)[i]); - hash = hash * FNV64_PRIME; - } - FOR_EACH(i, 0, sizeof(key->cp)) { - hash = hash ^ (uint64_t)((unsigned char)((const char*)&key->cp)[i]); - hash = hash * FNV64_PRIME; - } - FOR_EACH(i, 0, sizeof(key->delta)) { - hash = hash ^ (uint64_t)((unsigned char)((const char*)&key->delta)[i]); - hash = hash * FNV64_PRIME; - } - hash = hash ^ (uint64_t)((unsigned char)(key->Tinit == 0)); - hash = hash * FNV64_PRIME; - FOR_EACH(i, 0, key->Tinit ? strlen(key->Tinit) * sizeof(*key->Tinit) : 0) { - hash = hash ^ (uint64_t)((unsigned char)((const char*)key->Tinit)[i]); - hash = hash * FNV64_PRIME; - } - hash = hash ^ (uint64_t)((unsigned char)(key->Temp == 0)); - hash = hash * FNV64_PRIME; - FOR_EACH(i, 0, key->Temp ? strlen(key->Temp) * sizeof(*key->Temp) : 0) { - hash = hash ^ (uint64_t)((unsigned char)((const char*)key->Temp)[i]); - hash = hash * FNV64_PRIME; - } - FOR_EACH(i, 0, sizeof(key->has_power)) { - hash = hash ^ (uint64_t) ((unsigned char) ((const char*) &key->has_power)[i]); - hash = hash * FNV64_PRIME; - } - if (!key->has_power) return hash; - FOR_EACH(i, 0, strlen(key->power) * sizeof(*key->power)) { - hash = hash ^ (uint64_t)((unsigned char)((const char*)key->power)[i]); - hash = hash * FNV64_PRIME; - } - return hash; -#else -#error "Unexpected architecture" -#endif + str_release(&bound->name); } -struct h_boundary { - char name[32]; - unsigned mat_id; - double emissivity; - double specular_fraction; - double hc; - double hc_max; - char* T; - int has_emissivity, has_hc; -}; -#define NULL_HBOUND__ { "", UINT_MAX, 0, 0, 0, 0, NULL, 0, 0} -static const struct h_boundary NULL_HBOUND = NULL_HBOUND__; - -static void -print_h_boundary - (FILE* stream, +static res_T +str_print_h_boundary + (struct str* str, const struct h_boundary* b, const enum description_type type) { - ASSERT(stream && b - && (type == DESC_BOUND_H_FOR_SOLID || type == DESC_BOUND_H_FOR_FLUID)); - fprintf(stream, - "H boundary %s for %s: emissivity=%g specular_fraction=%g hc=%g hc_max=%g T='%s'\n", - b->name, - (type == DESC_BOUND_H_FOR_SOLID ? "solid" : "fluid"), - (b->has_emissivity ? b->emissivity : 0), - (b->has_emissivity ? b->specular_fraction : 0), - (b->has_hc ? b->hc : 0), - (b->has_hc ? b->hc_max : 0), - (b->T ? b->T : "0")); + res_T res = RES_OK; + ASSERT(str && b && DESC_IS_H(type)); + STR_APPEND_PRINTF(str, + "H boundary for %s '%s': emissivity=%g specular_fraction=%g hc=%g T=%g " + "(using medium %u as external medium)", + ARG7( (type == DESC_BOUND_H_FOR_SOLID ? "solid" : "fluid"), str_cget(&b->name), + b->emissivity, b->specular_fraction, b->hc, b->imposed_temperature, b->mat_id ) ); +end: + return res; +error: + goto end; } -static char -eq_h_boundary - (const struct h_boundary* a, - const struct h_boundary* b) +static FINLINE res_T +cp_h_boundary(struct h_boundary* dst, const struct h_boundary* src) { - if (a->mat_id != b->mat_id - || strcmp(a->name, b->name) - || a->specular_fraction != b->specular_fraction - || strcmp(a->T, b->T) - || a->has_emissivity != b->has_emissivity - || a->has_hc != b->has_hc) - return 0; - if (a->has_emissivity && a->emissivity != b->emissivity) return 0; - if (a->has_hc && (a->hc != b->hc || a->hc_max != b->hc_max)) return 0; - return 1; -} - -static size_t -hash_h_boundary(const struct h_boundary* key) -{ -#ifdef ARCH_32BITS - /* 32-bits Fowler/Noll/Vo hash function */ - const uint32_t FNV32_PRIME = - (uint32_t) (((uint32_t) 1 << 24) + ((uint32_t) 1 << 8) + 0x93); - const uint32_t OFFSET32_BASIS = 2166136261u; - uint32_t hash = OFFSET32_BASIS; - size_t i; - ASSERT(key); - FOR_EACH(i, 0, sizeof(key->name)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->name)[i]); - hash = hash * FNV32_PRIME; - } - FOR_EACH(i, 0, sizeof(key->mat_id)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->mat_id)[i]); - hash = hash * FNV32_PRIME; - } - FOR_EACH(i, 0, sizeof(key->emissivity)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->emissivity)[i]); - hash = hash * FNV32_PRIME; - } - FOR_EACH(i, 0, sizeof(key->specular_fraction)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->specular_fraction)[i]); - hash = hash * FNV32_PRIME; - } - FOR_EACH(i, 0, strlen(key->T) * sizeof(*key->T)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)key->T)[i]); - hash = hash * FNV32_PRIME; - } - FOR_EACH(i, 0, sizeof(key->has_emissivity)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->has_emissivity)[i]); - hash = hash * FNV32_PRIME; - } - FOR_EACH(i, 0, sizeof(key->has_hc)) { - hash = hash ^ (uint32_t) ((unsigned char) ((const char*) &key->has_hc)[i]); - hash = hash * FNV32_PRIME; - } - if (!key->has_hc) return hash; - FOR_EACH(i, 0, sizeof(key->hc)) { - hash = hash ^ (uint32_t) ((unsigned char) ((const char*) &key->hc)[i]); - hash = hash * FNV32_PRIME; - } - FOR_EACH(i, 0, sizeof(key->hc_max)) { - hash = hash ^ (uint32_t) ((unsigned char) ((const char*) &key->hc_max)[i]); - hash = hash * FNV32_PRIME; - } - return hash; -#elif defined(ARCH_64BITS) - /* 64-bits Fowler/Noll/Vo hash function */ - const uint64_t FNV64_PRIME = - (uint64_t) (((uint64_t) 1 << 40) + ((uint64_t) 1 << 8) + 0xB3); - const uint64_t OFFSET64_BASIS = (uint64_t) 14695981039346656037u; - uint64_t hash = OFFSET64_BASIS; - size_t i; - ASSERT(key); - FOR_EACH(i, 0, sizeof(key->name)) { - hash = hash ^ (uint64_t)((unsigned char)((const char*)&key->name)[i]); - hash = hash * FNV64_PRIME; - } - FOR_EACH(i, 0, sizeof(key->mat_id)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->mat_id)[i]); - hash = hash * FNV64_PRIME; - } - FOR_EACH(i, 0, sizeof(key->emissivity)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->emissivity)[i]); - hash = hash * FNV64_PRIME; - } - FOR_EACH(i, 0, sizeof(key->specular_fraction)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->specular_fraction)[i]); - hash = hash * FNV64_PRIME; - } - FOR_EACH(i, 0, strlen(key->T) * sizeof(*key->T)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)key->T)[i]); - hash = hash * FNV64_PRIME; - } - FOR_EACH(i, 0, sizeof(key->has_emissivity)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->has_emissivity)[i]); - hash = hash * FNV64_PRIME; - } - FOR_EACH(i, 0, sizeof(key->has_hc)) { - hash = hash ^ (uint32_t) ((unsigned char) ((const char*) &key->has_hc)[i]); - hash = hash * FNV64_PRIME; - } - if (!key->has_hc) return hash; - FOR_EACH(i, 0, sizeof(key->hc)) { - hash = hash ^ (uint32_t) ((unsigned char) ((const char*) &key->hc)[i]); - hash = hash * FNV64_PRIME; - } - FOR_EACH(i, 0, sizeof(key->hc_max)) { - hash = hash ^ (uint32_t) ((unsigned char) ((const char*) &key->hc_max)[i]); - hash = hash * FNV64_PRIME; - } - return hash; -#else -#error "Unexpected architecture" -#endif + dst->specular_fraction = src->specular_fraction; + dst->imposed_temperature = src->imposed_temperature; + dst->emissivity = src->emissivity; + dst->hc = src->hc; + dst->mat_id = src->mat_id; + return str_copy(&dst->name, &src->name); } struct t_boundary { - char name[32]; - unsigned mat_id; - char* T; - struct te_expr* te_temperature; + struct str name; + double emissivity; + double specular_fraction; double hc; - double hc_max; - int has_hc; + double imposed_temperature; + unsigned mat_id; }; -#define NULL_TBOUND__ { "", UINT_MAX, NULL, NULL, 0, 0, 0} -static const struct t_boundary NULL_TBOUND = NULL_TBOUND__; -static void -print_t_boundary - (FILE* stream, - const struct t_boundary* b, - const enum description_type type) +static FINLINE void +init_t(struct mem_allocator* allocator, struct t_boundary* dst) +{ + str_init(allocator, &dst->name); + dst->emissivity = 0; + dst->specular_fraction = 0; + dst->hc = 0; + dst->imposed_temperature = -1; + dst->mat_id = UINT_MAX; +} + +static FINLINE void +release_t_boundary(struct t_boundary* bound) { - ASSERT(stream && b - && (type == DESC_BOUND_T_FOR_SOLID || type == DESC_BOUND_T_FOR_FLUID)); - fprintf(stream, - "T boundary %s for %s: hc=%g hc_max=%g T='%s'\n", - b->name, - (type == DESC_BOUND_T_FOR_SOLID ? "solid" : "fluid"), - (b->has_hc ? b->hc : 0), - (b->has_hc ? b->hc_max : 0), - (b->T ? b->T : "0")); + str_release(&bound->name); } -static char -eq_t_boundary - (const struct t_boundary* a, +static res_T +str_print_t_boundary + (struct str* str, const struct t_boundary* b, const enum description_type type) { - ASSERT(type == DESC_BOUND_T_FOR_SOLID || type == DESC_BOUND_T_FOR_FLUID); - /* These fields are meaningful by both types */ - if (strcmp(a->name, b->name)) - return 0; - if (a->mat_id != b->mat_id || strcmp(a->T, b->T)) - return 0; - if (type != DESC_BOUND_T_FOR_FLUID) - return 1; - /* These ones are only relevant for fluids */ - if (a->has_hc != b->has_hc) - return 0; - if (a->has_hc && (a->hc != b->hc || a->hc_max != b->hc_max)) - return 0; - return 1; + res_T res = RES_OK; + ASSERT(str && b && DESC_IS_T(type)); + STR_APPEND_PRINTF(str, "T boundary for %s '%s': T=%g ", + ARG3( (type == DESC_BOUND_T_FOR_SOLID ? "solid" : "fluid"), + str_cget(&b->name), b->imposed_temperature ) ); + + if(type == DESC_BOUND_T_FOR_FLUID) { + STR_APPEND_PRINTF(str, "emissivity=%g, specular_fraction=%g hc=%g ", + ARG3( b->emissivity, b->specular_fraction, b->hc ) ); + } + STR_APPEND_PRINTF(str, "(using medium %u as external medium)", + ARG1( b->mat_id ) ); +end: + return res; +error: + goto end; } -static size_t -hash_t_boundary - (const struct t_boundary* key, - const enum description_type type) +static FINLINE res_T +cp_t_boundary(struct t_boundary* dst, const struct t_boundary* src) { - (void)type; - ASSERT(type == DESC_BOUND_T_FOR_SOLID || type == DESC_BOUND_T_FOR_FLUID); -#ifdef ARCH_32BITS - /* 32-bits Fowler/Noll/Vo hash function */ - const uint32_t FNV32_PRIME = - (uint32_t) (((uint32_t) 1 << 24) + ((uint32_t) 1 << 8) + 0x93); - const uint32_t OFFSET32_BASIS = 2166136261u; - uint32_t hash = OFFSET32_BASIS; - size_t i; - ASSERT(key); - FOR_EACH(i, 0, sizeof(key->name)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->name)[i]); - hash = hash * FNV32_PRIME; - } - FOR_EACH(i, 0, sizeof(key->mat_id)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->mat_id)[i]); - hash = hash * FNV32_PRIME; - } - FOR_EACH(i, 0, sizeof(key->has_hc)) { - hash = hash ^ (uint32_t) ((unsigned char) ((const char*) &key->has_hc)[i]); - hash = hash * FNV32_PRIME; - } - FOR_EACH(i, 0, strlen(key->T) * sizeof(*key->T)) { - hash = hash ^ (uint32_t) ((unsigned char) ((const char*) key->T)[i]); - hash = hash * FNV32_PRIME; - } - if (!key->has_hc) return hash; - FOR_EACH(i, 0, sizeof(key->hc)) { - hash = hash ^ (uint32_t) ((unsigned char) ((const char*) &key->hc)[i]); - hash = hash * FNV32_PRIME; - } - FOR_EACH(i, 0, sizeof(key->hc_max)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->hc_max)[i]); - hash = hash * FNV32_PRIME; - } - return hash; -#elif defined(ARCH_64BITS) - /* 64-bits Fowler/Noll/Vo hash function */ - const uint64_t FNV64_PRIME = - (uint64_t) (((uint64_t) 1 << 40) + ((uint64_t) 1 << 8) + 0xB3); - const uint64_t OFFSET64_BASIS = (uint64_t) 14695981039346656037u; - uint64_t hash = OFFSET64_BASIS; - size_t i; - ASSERT(key); - FOR_EACH(i, 0, sizeof(key->name)) { - hash = hash ^ (uint64_t)((unsigned char)((const char*)&key->name)[i]); - hash = hash * FNV64_PRIME; - } - FOR_EACH(i, 0, sizeof(key->mat_id)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->mat_id)[i]); - hash = hash * FNV64_PRIME; - } - FOR_EACH(i, 0, sizeof(key->has_hc)) { - hash = hash ^ (uint32_t) ((unsigned char) ((const char*) &key->has_hc)[i]); - hash = hash * FNV64_PRIME; - } - FOR_EACH(i, 0, strlen(key->T) * sizeof(*key->T)) { - hash = hash ^ (uint32_t) ((unsigned char) ((const char*) key->T)[i]); - hash = hash * FNV64_PRIME; - } - if (!key->has_hc) return hash; - FOR_EACH(i, 0, sizeof(key->hc)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->hc)[i]); - hash = hash * FNV64_PRIME; - } - FOR_EACH(i, 0, sizeof(key->hc_max)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->hc_max)[i]); - hash = hash * FNV64_PRIME; - } - return hash; -#else -#error "Unexpected architecture" -#endif + dst->emissivity = src->emissivity; + dst->specular_fraction = src->specular_fraction; + dst->hc = src->hc; + dst->imposed_temperature = src->imposed_temperature; + dst->mat_id = src->mat_id; + return str_copy(&dst->name, &src->name); } struct f_boundary { - char name[32]; + struct str name; + double imposed_flux; unsigned mat_id; - char* flux; - struct te_expr* te_flux; }; -#define NULL_FBOUND__ { "", UINT_MAX, NULL, NULL} -static const struct f_boundary NULL_FBOUND = NULL_FBOUND__; -static void -print_f_boundary - (FILE* stream, - const struct f_boundary* b, - const enum description_type type) +static FINLINE void +init_f(struct mem_allocator* allocator, struct f_boundary* dst) { - ASSERT(stream && b && type == DESC_BOUND_F_FOR_SOLID); (void)type; - fprintf(stream, - "F boundary %s for SOLID: flux='%s'\n", - b->name, - (b->flux ? b->flux : "0")); + str_init(allocator, &dst->name); + dst->mat_id = UINT_MAX; + dst->imposed_flux = -1; } -static char -eq_f_boundary(const struct f_boundary* a, const struct f_boundary* b) +static FINLINE void +release_f_boundary(struct f_boundary* bound) { - return (a->mat_id == b->mat_id - && 0 == strcmp(a->name, b->name) - && 0 == strcmp(a->flux, b->flux)); + str_release(&bound->name); } -static size_t -hash_f_boundary(const struct f_boundary* key) +static res_T +str_print_f_boundary + (struct str* str, + const struct f_boundary* b) { -#ifdef ARCH_32BITS - /* 32-bits Fowler/Noll/Vo hash function */ - const uint32_t FNV32_PRIME = - (uint32_t) (((uint32_t) 1 << 24) + ((uint32_t) 1 << 8) + 0x93); - const uint32_t OFFSET32_BASIS = 2166136261u; - uint32_t hash = OFFSET32_BASIS; - size_t i; - ASSERT(key); - FOR_EACH(i, 0, sizeof(key->name)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->name)[i]); - hash = hash * FNV32_PRIME; - } - FOR_EACH(i, 0, sizeof(key->mat_id)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->mat_id)[i]); - hash = hash * FNV32_PRIME; - } - FOR_EACH(i, 0, strlen(key->flux) * sizeof(*key->flux)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)key->flux)[i]); - hash = hash * FNV32_PRIME; - } - return hash; -#elif defined(ARCH_64BITS) - /* 64-bits Fowler/Noll/Vo hash function */ - const uint64_t FNV64_PRIME = - (uint64_t) (((uint64_t) 1 << 40) + ((uint64_t) 1 << 8) + 0xB3); - const uint64_t OFFSET64_BASIS = (uint64_t) 14695981039346656037u; - uint64_t hash = OFFSET64_BASIS; - size_t i; - ASSERT(key); - FOR_EACH(i, 0, sizeof(key->name)) { - hash = hash ^ (uint64_t)((unsigned char)((const char*)&key->name)[i]); - hash = hash * FNV64_PRIME; - } - FOR_EACH(i, 0, sizeof(key->mat_id)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->mat_id)[i]); - hash = hash * FNV64_PRIME; - } - FOR_EACH(i, 0, strlen(key->flux) * sizeof(*key->flux)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)key->flux)[i]); - hash = hash * FNV64_PRIME; - } - return hash; -#else -#error "Unexpected architecture" -#endif + res_T res = RES_OK; + ASSERT(str && b); + STR_APPEND_PRINTF(str, + "F boundary for SOLID '%s': flux=%g (using medium %u as external medium)", + ARG3( str_cget(&b->name), b->imposed_flux, b->mat_id ) ); +end: + return res; +error: + goto end; +} + +static FINLINE res_T +cp_f_boundary(struct f_boundary* dst, const struct f_boundary* src) +{ + dst->imposed_flux = src->imposed_flux; + dst->mat_id = src->mat_id; + return str_copy(&dst->name, &src->name); } struct solid_fluid_connect { - char name[32]; + struct str name; double emissivity; double specular_fraction; double hc; - double hc_max; - int has_emissivity, has_hc; + unsigned connection_id; }; -#define NULL_SFCONNECT__ { "", 0, 0, 0, 0, 0, 0} -static const struct solid_fluid_connect NULL_SFCONNECT = NULL_SFCONNECT__; -static char -eq_sf_connect(const struct solid_fluid_connect* a, const struct solid_fluid_connect* b) +static FINLINE void +release_sf_connect(struct solid_fluid_connect* connect) { - return (char)(!strcmp(a->name, b->name) - && a->emissivity == b->emissivity - && a->specular_fraction == b->specular_fraction - && a->hc == b->hc - && a->hc_max == b->hc_max - && a->has_emissivity == b->has_emissivity - && a->has_hc == b->has_hc); + str_release(&connect->name); } -static size_t -hash_sf_connect(const struct solid_fluid_connect* key) +static FINLINE void +init_sf(struct mem_allocator* allocator, struct solid_fluid_connect* dst) { -#ifdef ARCH_32BITS - /* 32-bits Fowler/Noll/Vo hash function */ - const uint32_t FNV32_PRIME = - (uint32_t) (((uint32_t) 1 << 24) + ((uint32_t) 1 << 8) + 0x93); - const uint32_t OFFSET32_BASIS = 2166136261u; - uint32_t hash = OFFSET32_BASIS; - size_t i; - ASSERT(key); - FOR_EACH(i, 0, sizeof(key->name)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->name)[i]); - hash = hash * FNV32_PRIME; - } - FOR_EACH(i, 0, sizeof(key->emissivity)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->emissivity)[i]); - hash = hash * FNV32_PRIME; - } - FOR_EACH(i, 0, sizeof(key->specular_fraction)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->specular_fraction)[i]); - hash = hash * FNV32_PRIME; - } - FOR_EACH(i, 0, sizeof(key->hc)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->hc)[i]); - hash = hash * FNV32_PRIME; - } - FOR_EACH(i, 0, sizeof(key->hc_max)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->hc_max)[i]); - hash = hash * FNV32_PRIME; - } - FOR_EACH(i, 0, sizeof(key->has_emissivity)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->has_emissivity)[i]); - hash = hash * FNV32_PRIME; - } - FOR_EACH(i, 0, sizeof(key->has_hc)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->has_hc)[i]); - hash = hash * FNV32_PRIME; - } - return hash; -#elif defined(ARCH_64BITS) - /* 64-bits Fowler/Noll/Vo hash function */ - const uint64_t FNV64_PRIME = - (uint64_t) (((uint64_t) 1 << 40) + ((uint64_t) 1 << 8) + 0xB3); - const uint64_t OFFSET64_BASIS = (uint64_t) 14695981039346656037u; - uint64_t hash = OFFSET64_BASIS; - size_t i; - ASSERT(key); - FOR_EACH(i, 0, sizeof(key->name)) { - hash = hash ^ (uint64_t)((unsigned char)((const char*)&key->name)[i]); - hash = hash * FNV64_PRIME; - } - FOR_EACH(i, 0, sizeof(key->emissivity)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->emissivity)[i]); - hash = hash * FNV64_PRIME; - } - FOR_EACH(i, 0, sizeof(key->specular_fraction)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->specular_fraction)[i]); - hash = hash * FNV64_PRIME; - } - FOR_EACH(i, 0, sizeof(key->hc)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->hc)[i]); - hash = hash * FNV64_PRIME; - } - FOR_EACH(i, 0, sizeof(key->hc_max)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->hc_max)[i]); - hash = hash * FNV64_PRIME; - } - FOR_EACH(i, 0, sizeof(key->has_emissivity)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->has_emissivity)[i]); - hash = hash * FNV64_PRIME; - } - FOR_EACH(i, 0, sizeof(key->has_hc)) { - hash = hash ^ (uint32_t)((unsigned char)((const char*)&key->has_hc)[i]); - hash = hash * FNV64_PRIME; - } - return hash; -#else -#error "Unexpected architecture" -#endif + str_init(allocator, &dst->name); + dst->emissivity = 0; + dst->specular_fraction = 0; + dst->hc = 0; + dst->connection_id = UINT_MAX; +} + +static res_T +str_print_sf_connect + (struct str* str, + const struct solid_fluid_connect* c) +{ + res_T res = RES_OK; + ASSERT(str && c); + STR_APPEND_PRINTF(str, "Solid-Fluid connection '%s':", ARG1( str_cget(&c->name) ) ); + STR_APPEND_PRINTF(str, " emissivity=%g, specular_fraction=%g hc=%g", + ARG3( c->emissivity, c->specular_fraction, c->hc ) ); +end: + return res; +error: + goto end; +} + +static FINLINE res_T +cp_sf_connect + (struct solid_fluid_connect* dst, const struct solid_fluid_connect* src) +{ + dst->connection_id = src->connection_id; + dst->specular_fraction = src->specular_fraction; + dst->emissivity = src->emissivity; + dst->hc = src->hc; + return str_copy(&dst->name, &src->name); +} + +static FINLINE res_T +cp_release_sf_connect + (struct solid_fluid_connect* dst, struct solid_fluid_connect* src) +{ + return cp_sf_connect(dst, src); } struct description { enum description_type type; - union d { - struct mat_fluid fluid; - struct mat_solid solid; + union { + struct fluid fluid; + struct solid solid; struct t_boundary t_boundary; struct f_boundary f_boundary; struct h_boundary h_boundary; @@ -876,163 +427,355 @@ struct description { } d; }; -FINLINE void -init_description(struct description* desc) { +static FINLINE res_T +init_description(struct mem_allocator* alloc, struct description* desc) +{ ASSERT(desc); + (void)alloc; desc->type = DESCRIPTION_TYPE_COUNT__; + return RES_OK; } -static INLINE void -print_description - (FILE* stream, - struct description* desc) +static FINLINE void +release_description(struct description* desc) +{ + switch (desc->type) { + case DESC_MAT_SOLID: + release_solid(&desc->d.solid); + break; + case DESC_MAT_FLUID: + release_fluid(&desc->d.fluid); + break; + case DESC_BOUND_H_FOR_SOLID: + case DESC_BOUND_H_FOR_FLUID: + release_h_boundary(&desc->d.h_boundary); + break; + case DESC_BOUND_T_FOR_SOLID: + case DESC_BOUND_T_FOR_FLUID: + release_t_boundary(&desc->d.t_boundary); + break; + case DESC_BOUND_F_FOR_SOLID: + release_f_boundary(&desc->d.f_boundary); + break; + case DESC_SOLID_FLUID_CONNECT: + release_sf_connect(&desc->d.sf_connect); + break; + default: + FATAL("error:" STR(__FILE__) ":" STR(__LINE__)": Invalid type.\n"); + } +} + +static INLINE res_T +str_print_description + (struct str* str, + const unsigned rank, + const struct description* desc) { - ASSERT(stream && desc); + res_T res = RES_OK; + ASSERT(str && desc); + str_clear(str); + ERR(str_printf(str, "Description %u: ", rank)); switch (desc->type) { case DESC_MAT_SOLID: - print_solid(stream, &desc->d.solid); + ERR(str_print_solid(str, &desc->d.solid)); break; case DESC_MAT_FLUID: - print_fluid(stream, &desc->d.fluid); + ERR(str_print_fluid(str, &desc->d.fluid)); break; case DESC_BOUND_T_FOR_SOLID: case DESC_BOUND_T_FOR_FLUID: - print_t_boundary(stream, &desc->d.t_boundary, desc->type); + ERR(str_print_t_boundary(str, &desc->d.t_boundary, desc->type)); break; case DESC_BOUND_H_FOR_SOLID: case DESC_BOUND_H_FOR_FLUID: - print_h_boundary(stream, &desc->d.h_boundary, desc->type); + ERR(str_print_h_boundary(str, &desc->d.h_boundary, desc->type)); break; case DESC_BOUND_F_FOR_SOLID: - print_f_boundary(stream, &desc->d.f_boundary, desc->type); + ERR(str_print_f_boundary(str, &desc->d.f_boundary)); + break; + case DESC_SOLID_FLUID_CONNECT: + ERR(str_print_sf_connect(str, &desc->d.sf_connect)); break; - default: FATAL("Invalid type.\n"); + default: + FATAL("error:" STR(__FILE__) ":" STR(__LINE__)": Invalid type.\n"); + } +end: + return res; +error: + goto end; +} + +static INLINE const struct str* +get_description_name + (const struct description* desc) +{ + ASSERT(desc); + switch (desc->type) { + case DESC_MAT_SOLID: + return &desc->d.solid.name; + case DESC_MAT_FLUID: + return &desc->d.fluid.name; + case DESC_BOUND_T_FOR_SOLID: + case DESC_BOUND_T_FOR_FLUID: + return &desc->d.t_boundary.name; + case DESC_BOUND_H_FOR_SOLID: + case DESC_BOUND_H_FOR_FLUID: + return &desc->d.h_boundary.name; + case DESC_BOUND_F_FOR_SOLID: + return &desc->d.f_boundary.name; + case DESC_SOLID_FLUID_CONNECT: + return &desc->d.sf_connect.name; + default: + FATAL("error:" STR(__FILE__) ":" STR(__LINE__)": Invalid type.\n"); } } -static INLINE char -eq_description(const struct description* a, const struct description* b) +static FINLINE res_T +cp_description + (struct description* dst, + const struct description* src) { - if (a->type != b->type) return 0; - switch (a->type) { + res_T res = RES_OK; + ASSERT(src && dst); + switch (src->type) { case DESC_MAT_SOLID: - return eq_solid(&a->d.solid, &b->d.solid); + if(dst->type == DESCRIPTION_TYPE_COUNT__) { + dst->type = src->type; + init_solid(src->d.solid.name.allocator, &dst->d.solid); + } + ERR(cp_solid(&dst->d.solid, &src->d.solid)); + break; case DESC_MAT_FLUID: - return eq_fluid(&a->d.fluid, &b->d.fluid); + if(dst->type == DESCRIPTION_TYPE_COUNT__) { + dst->type = src->type; + init_fluid(src->d.fluid.name.allocator, &dst->d.fluid); + } + ERR(cp_fluid(&dst->d.fluid, &src->d.fluid)); + break; case DESC_BOUND_H_FOR_SOLID: case DESC_BOUND_H_FOR_FLUID: - return eq_h_boundary(&a->d.h_boundary, &b->d.h_boundary); + if(dst->type == DESCRIPTION_TYPE_COUNT__) { + dst->type = src->type; + init_h(src->d.h_boundary.name.allocator, &dst->d.h_boundary); + } + ERR(cp_h_boundary(&dst->d.h_boundary, &src->d.h_boundary)); + break; case DESC_BOUND_T_FOR_SOLID: case DESC_BOUND_T_FOR_FLUID: - return eq_t_boundary(&a->d.t_boundary, &b->d.t_boundary, a->type); + if(dst->type == DESCRIPTION_TYPE_COUNT__) { + dst->type = src->type; + init_t(src->d.t_boundary.name.allocator, &dst->d.t_boundary); + } + ERR(cp_t_boundary(&dst->d.t_boundary, &src->d.t_boundary)); + break; case DESC_BOUND_F_FOR_SOLID: - return eq_f_boundary(&a->d.f_boundary, &b->d.f_boundary); + if(dst->type == DESCRIPTION_TYPE_COUNT__) { + dst->type = src->type; + init_f(src->d.f_boundary.name.allocator, &dst->d.f_boundary); + } + ERR(cp_f_boundary(&dst->d.f_boundary, &src->d.f_boundary)); + break; case DESC_SOLID_FLUID_CONNECT: - return eq_sf_connect(&a->d.sf_connect, &b->d.sf_connect); - default: FATAL("Invalid type.\n"); + if(dst->type == DESCRIPTION_TYPE_COUNT__) { + dst->type = src->type; + init_sf(src->d.sf_connect.name.allocator, &dst->d.sf_connect); + } + ERR(cp_sf_connect(&dst->d.sf_connect, &src->d.sf_connect)); + break; + default: + FATAL("error:" STR(__FILE__) ":" STR(__LINE__)": Invalid type.\n"); } +end: + return res; +error: + goto end; } -static INLINE size_t -hash_description(const struct description* key) +static FINLINE void +description_get_medium_id + (const struct description* desc, + unsigned* id) { - switch (key->type) { + ASSERT(desc && id); + switch (desc->type) { case DESC_MAT_SOLID: - return hash_solid(&key->d.solid); + *id = desc->d.solid.solid_id; + return; case DESC_MAT_FLUID: - return hash_fluid(&key->d.fluid); + *id = desc->d.fluid.fluid_id; + return; case DESC_BOUND_H_FOR_SOLID: case DESC_BOUND_H_FOR_FLUID: - return hash_h_boundary(&key->d.h_boundary); + *id = desc->d.h_boundary.mat_id; + return; case DESC_BOUND_T_FOR_SOLID: case DESC_BOUND_T_FOR_FLUID: - return hash_t_boundary(&key->d.t_boundary, key->type); + *id = desc->d.t_boundary.mat_id; + return; case DESC_BOUND_F_FOR_SOLID: - return hash_f_boundary(&key->d.f_boundary); - case DESC_SOLID_FLUID_CONNECT: - return hash_sf_connect(&key->d.sf_connect); - default: FATAL("Invalid type.\n"); + *id = desc->d.f_boundary.mat_id; + return; + case DESC_SOLID_FLUID_CONNECT: /* No medium linked to SF */ + default: + FATAL("error:" STR(__FILE__) ":" STR(__LINE__)": Invalid type.\n"); } } +enum stardis_output_fmt { + STARDIS_RENDERING_OUTPUT_FILE_FMT_VTK, + STARDIS_RENDERING_OUTPUT_FILE_FMT_HT +}; + struct camera { double pos[3]; double tgt[3]; double up[3]; + enum stardis_output_fmt fmt; double fov; + double time_range[2]; unsigned spp; - unsigned img[2]; - union { /* Trick to allow static initialization with INF */ - uint64_t t; - double time; - } u; + unsigned img_width, img_height; + int auto_look_at; }; -#define NULL_CAMERA__ {\ - {1,1,1},{0,0,0},{0,0,1},30,4,{640,480},\ - { 0x7FF0000000000000 } /* time=INF */\ -} -static const struct camera NULL_CAMERA = NULL_CAMERA__; - -struct solve_boundary { - size_t* primitives; - enum sdis_side* sides; + +static INLINE void +init_camera(struct camera* cam) { + d3(cam->pos, STARDIS_DEFAULT_RENDERING_POS); + d3(cam->tgt, STARDIS_DEFAULT_RENDERING_TGT); + d3(cam->up, STARDIS_DEFAULT_RENDERING_UP); + cam->fmt = STARDIS_DEFAULT_RENDERING_OUTPUT_FILE_FMT; + cam->fov = STARDIS_DEFAULT_RENDERING_FOV; + cam->spp = STARDIS_DEFAULT_RENDERING_SPP; + cam->img_width = STARDIS_DEFAULT_RENDERING_IMG_WIDTH; + cam->img_height = STARDIS_DEFAULT_RENDERING_IMG_HEIGHT; + d2(cam->time_range, STARDIS_DEFAULT_RENDERING_TIME); + cam->auto_look_at = 1; +} + +static INLINE void +log_err_fn(const char* msg, void* ctx) +{ + ASSERT(msg); + (void)ctx; +#ifdef OS_WINDOWS + fprintf(stderr, "error: %s", msg); +#else + fprintf(stderr, "\x1b[31merror:\x1b[0m %s", msg); +#endif +} + +static INLINE void +log_warn_fn(const char* msg, void* ctx) +{ + ASSERT(msg); + (void)ctx; +#ifdef OS_WINDOWS + fprintf(stderr, "warning: %s", msg); +#else + fprintf(stderr, "\x1b[33mwarning:\x1b[0m %s", msg); +#endif +} + +static INLINE void +log_prt_fn(const char* msg, void* ctx) +{ + ASSERT(msg); + (void)ctx; +#ifdef OS_WINDOWS + fprintf(stderr, "message: %s", msg); +#else + fprintf(stderr, "\x1b[32moutput:\x1b[0m %s", msg); +#endif +} + +struct counts { + unsigned smed_count, fmed_count, tbound_count, hbound_count, + fbound_count, sfconnect_count; +}; +#define COUNTS_NULL__ {\ + 0, 0, 0, 0, 0, 0\ + } + +/* Type to store the primitives of a compute region */ +#define DARRAY_NAME sides +#define DARRAY_DATA enum sdis_side +#include <rsys/dynamic_array.h> + +#define DARRAY_NAME descriptions +#define DARRAY_DATA struct description +#define DARRAY_FUNCTOR_INIT init_description +#define DARRAY_FUNCTOR_COPY cp_description +#define DARRAY_FUNCTOR_RELEASE release_description +#include <rsys/dynamic_array.h> + +struct compute_surface { + struct darray_size_t primitives; + struct darray_sides sides; + struct darray_uint err_triangles; }; struct stardis { struct geometry geometry; - struct description* descriptions; /*array of materials and boundaries */ - - double probe[4]; /* x,y,z,t of probe when mode is PROBE_COMPUTE */ + struct sdis_scene* sdis_scn; /* The solver scene */ + struct darray_descriptions descriptions; /* Materials and boundaries */ + struct darray_media_ptr media; + struct senc3d_scene* senc3d_scn; + struct counts counts; + + double probe[3]; /* x,y,z of probe when mode is PROBE_COMPUTE */ + double time_range[2]; /* compute time */ struct camera camera; /* camera when mode is IR_COMPUTE */ - char solve_name[64]; /* medium name when mode is MEDIUM_COMPUTE, - boundary file name when BOUNDARY_COMPUTE*/ - struct solve_boundary boundary; /* Boundary to solve when mode - is BOUNDARY_COMPUTE */ - - size_t N; /*number of MC realizations*/ + struct str solve_name; /* medium name when mode is MEDIUM_COMPUTE, + boundary file name when [FLUX_]BOUNDARY_COMPUTE + map file name when MAP_COMPUTE */ + struct compute_surface compute_surface; /* 2D compute region when mode is + [FLUX_]BOUNDARY_COMPUTE + or MAP_COMPUTE */ + struct str paths_filename; + struct str bin_green_filename; + struct str end_paths_filename; + struct str chunks_prefix; + int mode; + size_t samples; unsigned nthreads; double scale_factor; - double radiative_temp[2]; - struct mem_allocator allocator; - int allocator_initialized; - enum sdis_heat_path_flag dump_paths; -}; -#define NULL_ALLOCATOR__ {NULL} -#define NULL_STARDIS__ {\ - NULL_GEOMETRY__, NULL,\ - {0,0,0,0}, NULL_CAMERA__, "", {NULL,NULL},\ - 0, 0, 0, {300,300}, NULL_ALLOCATOR__, 0, SDIS_HEAT_PATH_NONE} -static const struct stardis NULL_STARDIS = NULL_STARDIS__; - -enum content_type { - CONTENT_GEOMETRY, - CONTENT_CONNECTION, - CONTENT_BOUNDARY, - CONTENT_TYPES_COUNT + double ambient_temp, ref_temp; + struct mem_allocator* allocator; + struct logger* logger; + struct sdis_device* dev; + unsigned next_medium_id; + unsigned undefined_medium_behind_boundary_id; + int dump_paths; + int verbose; }; +static INLINE unsigned +allocate_stardis_medium_id(struct stardis* stardis) +{ + ASSERT(stardis); + return stardis->next_medium_id++; +} -extern res_T +extern LOCAL_SYM res_T stardis_init (const struct args* args, + struct logger* logger, + struct mem_allocator* allocator, struct stardis* stardis); -extern res_T -stardis_compute(struct stardis* stardis, enum stardis_mode mode); - -extern void -stardis_release(struct stardis* stardis); +extern LOCAL_SYM void +stardis_release + (struct stardis* stardis); -extern res_T -dump_vtk(FILE* output, const struct geometry* geometry); +extern LOCAL_SYM res_T +init_enclosures + (struct stardis* stardis); -extern res_T -read_stl - (const unsigned desc_id, - const enum content_type file_type, - const char* stl_filename, - struct htable_vertex* vertex2id, - struct htable_triangle* triangle2id, - struct stardis* stardis); +extern LOCAL_SYM res_T +validate_properties + (const unsigned itri, + const unsigned properties[SG3D_PROP_TYPES_COUNT__], + void* context, + int* properties_conflict_status); #endif /*STARDIS-APP_H*/ diff --git a/src/stardis-compute.c b/src/stardis-compute.c @@ -1,1995 +1,1047 @@ -/* Copyright (C) 2018 |Meso|Star> (contact@meso-star.com)*/ +/* Copyright (C) 2018-2020 |Meso|Star> (contact@meso-star.com) + * + * This program 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. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ #include "stardis-app.h" +#include "stardis-output.h" +#include "stardis-compute.h" +#include "stardis-fluid.h" +#include "stardis-solid.h" + #include <sdis.h> #include <sdis_version.h> + +#include <star/s3d.h> +#include <star/sg3d_sXd_helper.h> + #include <rsys/double3.h> #include <rsys/double2.h> -#include <rsys/dynamic_array_uint.h> - +#include <rsys/logger.h> #include <rsys/image.h> -#include<star/senc.h> - -#include <rsys/hash_table.h> -#define HTABLE_NAME vrtx_rank -#define HTABLE_DATA unsigned -#define HTABLE_KEY unsigned -#include <rsys/hash_table.h> - -#define HTABLE_NAME weigth -#define HTABLE_DATA double -#define HTABLE_KEY unsigned -#include <rsys/hash_table.h> - -struct w_ctx { - struct description* desc; - struct htable_weigth weigths; -}; - -/* A type to limit variable use in tinyexpr expressions */ -enum var_prohibited_t { - ALL_VARS_ALLOWED = 0, - T_PROHIBITED = BIT(4), - XYZ_PROHIBITED = BIT(5), - NO_VAR_ALLOWED = T_PROHIBITED | XYZ_PROHIBITED -}; - -static void -geometry_get_position - (const size_t ivert, - double pos[3], - void* context) -{ - struct geometry* geom = context; - pos[0] = geom->vertex[ivert].xyz[0]; - pos[1] = geom->vertex[ivert].xyz[1]; - pos[2] = geom->vertex[ivert].xyz[2]; -} - -static void -geometry_get_indices - (const size_t itri, - size_t ids[3], - void* context) -{ - struct geometry* geom = context; - ids[0] = geom->triangle[itri].indices.data[0]; - ids[1] = geom->triangle[itri].indices.data[1]; - ids[2] = geom->triangle[itri].indices.data[2]; -} - -static void -geometry_get_interface - (const size_t itri, - struct sdis_interface** interf, - void* context) -{ - struct geometry* geom = context; - *interf = geom->interf_bytrg[itri]; -} - -static res_T -compile_expr_to_fn - (struct te_expr** f, - const char* math_expr, - const int prohibited, - int* is_zero) -{ - te_variable vars[4] = { - TE_DEF_OFFSET("x", offsetof(struct sdis_rwalk_vertex, P[0])), - TE_DEF_OFFSET("y", offsetof(struct sdis_rwalk_vertex, P[1])), - TE_DEF_OFFSET("z", offsetof(struct sdis_rwalk_vertex, P[2])), - TE_DEF_OFFSET("t", offsetof(struct sdis_rwalk_vertex, time)) - }; - int fst = 0, lst = 4; - ASSERT(math_expr); - ASSERT(prohibited == (prohibited & NO_VAR_ALLOWED)); /* Use only defined flags */ - if (prohibited & XYZ_PROHIBITED) fst = 3; - if(prohibited & T_PROHIBITED) lst = 3; - *f = te_compile(math_expr, vars+fst, lst-fst, NULL); - if (!*f) { - if ((prohibited != ALL_VARS_ALLOWED) - && te_compile(math_expr, vars, 4, NULL)) - fprintf(stderr, "Expression %s use a probibited variable ", math_expr); - if (prohibited == NO_VAR_ALLOWED) - fprintf(stderr, "(no variable allowed)\n"); - else if (prohibited == XYZ_PROHIBITED) - fprintf(stderr, "(xyz prohibited)\n"); - else { - ASSERT(prohibited == T_PROHIBITED); - fprintf(stderr, "(t prohibited)\n"); - } - return RES_BAD_ARG; - } - if (is_zero) *is_zero = !((*f)->type == TE_CONSTANT && (*f)->v.value == 0); - return RES_OK; -} /******************************************************************************* - * Fluid data + * Local Functions ******************************************************************************/ -struct fluid { - char name[32]; - double cp; /* Calorific capacity */ - double rho; /* Volumic mass */ - /* Compute mode */ - int is_green, is_outside; - double t0; - /* TinyExpr stuff to compute temperature */ - struct te_expr *temp; - struct te_expr *t_init; - /* ID */ - unsigned id; -}; -static double -fluid_get_calorific_capacity - (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) -{ - const struct fluid* fluid_props = sdis_data_cget(data); - (void)vtx; - return fluid_props->cp; -} +struct filter_ctx { + const struct stardis* stardis; + unsigned prim; + const struct description* desc; + float pos[3]; + float dist; + int outside; + int probe_on_boundary; +}; -static double -fluid_get_volumic_mass - (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) -{ - const struct fluid* fluid_props = sdis_data_cget(data); - (void)vtx; - return fluid_props->rho; -} +#define FILTER_CTX_DEFAULT__ \ + { NULL, S3D_INVALID_ID, NULL, { 0, 0, 0 }, FLT_MAX, 0, 0 } -static double -fluid_get_temperature - (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +/* Filter used from a point query to determine not only one of the closest + * point, but the better one if there are more than one. In some circumstances + * it is not possible to determine the medium we are in using a given hit, but + * it is possible using another equidistant hit : + * + * + * P C + * +.............+---trg 1--- + * | + * medium 1 trg 2 medium 2 + * | + * + * C is the closest point from P, and 2 different hits at C are possible (one + * on each triangle). However, only hit on trg 2 allows to find out that P is + * in medium 1 using sign(PC.Ntrg1) as PC.Ntrg2 = 0. + * The following filter function aims at selecting the hit on trg2 regardless + * of the order in which the 2 triangles are checked. + * One unexpected case cannot be decided though, but it implies that the + * closest triangle has 2 different media on its sides and that P lies on the + * triangle's plane : + * + * P C medium 1 + * + +---trg--- + * medium 2 + */ +static int +hit_filter + (const struct s3d_hit* hit, + const float ray_org[3], + const float* invalid_, /* In closest_point queries ray_dir is not informed */ + void* ray_data, + void* filter_data) { - const struct fluid* fluid_props = sdis_data_cget(data); - char msg[128]; - ASSERT(fluid_props->t_init || fluid_props->temp); - if (fluid_props->is_green || vtx->time > fluid_props->t0) { - /* Always use temp for Green mode, regardless of time */ - if (fluid_props->temp) - return te_eval(fluid_props->temp, vtx); - else return -1; + struct filter_ctx* filter_ctx = ray_data; + float s, dir[3]; + enum senc3d_side hit_side; + struct s3d_attrib interf_pos; + const struct description* descriptions; + unsigned descr[SG3D_PROP_TYPES_COUNT__]; + const struct stardis* stardis; + + (void)ray_org; (void)invalid_; (void)filter_data; + ASSERT(hit && filter_ctx); + ASSERT(hit->uv[0] == CLAMP(hit->uv[0], 0, 1)); + ASSERT(hit->uv[1] == CLAMP(hit->uv[1], 0, 1)); + + if(filter_ctx->dist == hit->distance && filter_ctx->desc) { + /* Cannot improve: keep previous! + * Or we could end with a NULL desc */ + return 1; /* Skip */ } - /* Time is t0: use t_init */ - if (fluid_props->t_init) - return te_eval(fluid_props->t_init, vtx); - /* Must have had t_init defined: error! */ - if (fluid_props->name[0]) - sprintf(msg, - "fluid_get_temperature: getting undefined Tinit (fluid '%s')\n", - fluid_props->name); - else - sprintf(msg, "fluid_get_temperature: getting undefined Tinit\n"); - FATAL(msg); -} - -static void -release_fluid_data(void* f) -{ - struct fluid* fluid = (struct fluid*)f; - te_free(fluid->t_init); - te_free(fluid->temp); -} -/******************************************************************************* - * Solid data - ******************************************************************************/ -struct solid { - char name[32]; - double cp; /* Calorific capacity */ - double lambda; /* Conductivity */ - double rho; /* Volumic mass */ - double delta; /* Numerical parameter */ - /* Compute mode */ - int is_green, is_outside; - double t0; - /* TinyExpr stuff to compute temperature & power */ - struct te_expr *temp; - struct te_expr *t_init; - struct te_expr *power; - /* ID */ - unsigned id; -}; + stardis = filter_ctx->stardis; + descriptions = darray_descriptions_cdata_get(&stardis->descriptions); -static double -solid_get_calorific_capacity - (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) -{ - const struct solid* solid_props = sdis_data_cget(data); - (void)vtx; - return solid_props->cp; -} + CHK(s3d_primitive_get_attrib(&hit->prim, S3D_POSITION, hit->uv, &interf_pos) + == RES_OK); -static double -solid_get_thermal_conductivity - (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) -{ - const struct solid* solid_props = sdis_data_cget(data); - (void)vtx; - return solid_props->lambda; -} + if(hit->distance == 0) { + filter_ctx->prim = hit->prim.prim_id; + filter_ctx->desc = NULL; /* Not apply */ + f3_set(filter_ctx->pos, interf_pos.value); + filter_ctx->dist = hit->distance; + filter_ctx->outside = 0; + filter_ctx->probe_on_boundary = 1; + return 0; /* Keep */ + } -static double -solid_get_volumic_mass - (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) -{ - const struct solid* solid_props = sdis_data_cget(data); - (void)vtx; - return solid_props->rho; -} + /* Get the description IDs for this triangle */ + CHK(sg3d_geometry_get_unique_triangle_properties(stardis->geometry.sg3d, + hit->prim.prim_id, descr) == RES_OK); + + if(descr[SG3D_FRONT] == descr[SG3D_BACK]) { + /* Don't need to bother with sides */ + filter_ctx->prim = hit->prim.prim_id; + filter_ctx->desc = (descr[SG3D_FRONT] == SG3D_UNSPECIFIED_PROPERTY) + ? NULL : descriptions + descr[SG3D_FRONT]; + f3_set(filter_ctx->pos, interf_pos.value); + filter_ctx->dist = hit->distance; + filter_ctx->outside = (descr[SG3D_FRONT] == SG3D_UNSPECIFIED_PROPERTY); + filter_ctx->probe_on_boundary = 0; + return 0; /* Keep */ + } -static double -solid_get_delta - (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) -{ - const struct solid* solid_props = sdis_data_cget(data); - (void)vtx; - return solid_props->delta; -} + f3_sub(dir, interf_pos.value, ray_org); + s = f3_dot(dir, hit->normal); -#if Stardis_VERSION_MINOR == 3 -static double -solid_get_delta_boundary - (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) -{ - const struct solid* solid_props = sdis_data_cget(data); - return solid_props->delta - /* Emperical scale factor that ensures that delta_boundary > delta withouht - * being an exact multiple of delta. */ - /** 2.1 */; -} -#endif + if(s == 0) { + filter_ctx->prim = hit->prim.prim_id; + filter_ctx->desc = NULL; /* Cannot decide side */ + f3_set(filter_ctx->pos, interf_pos.value); + filter_ctx->dist = hit->distance; + filter_ctx->outside = 0; + filter_ctx->probe_on_boundary = 0; + } -static double -solid_get_temperature - (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) -{ - const struct solid* solid_props = sdis_data_cget(data); - char msg[128]; - ASSERT(solid_props->t_init || solid_props->temp); - if (solid_props->is_green || vtx->time > solid_props->t0) { - /* Always use temp for Green mode, regardless of time */ - if (solid_props->temp) - return te_eval(solid_props->temp, vtx); - else return -1; + /* Determine which side was hit + * Warning: following Embree 2 convention for geometrical normals, + * the Star3D hit normals are left-handed */ + hit_side = (s > 0) ? SG3D_BACK : SG3D_FRONT; + if(descr[hit_side] == SG3D_UNSPECIFIED_PROPERTY) { + filter_ctx->prim = hit->prim.prim_id; + filter_ctx->desc = NULL; + f3_set(filter_ctx->pos, interf_pos.value); + filter_ctx->dist = hit->distance; + filter_ctx->outside = 1; + filter_ctx->probe_on_boundary = 0; + } else { + filter_ctx->prim = hit->prim.prim_id; + filter_ctx->desc = descriptions + descr[hit_side]; + f3_set(filter_ctx->pos, interf_pos.value); + filter_ctx->dist = hit->distance; + filter_ctx->outside = 0; + filter_ctx->probe_on_boundary = 0; } - /* Time is t0: use t_init */ - if (solid_props->t_init) - return te_eval(solid_props->t_init, vtx); - /* Must have had t_init defined: error! */ - if (solid_props->name[0]) - sprintf(msg, - "solid_get_temperature: getting undefined Tinit (solid '%s')\n", - solid_props->name); - else - sprintf(msg, "solid_get_temperature: getting undefined Tinit\n"); - FATAL(msg); -} -static double -solid_get_power - (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) -{ - const struct solid* solid_props = sdis_data_cget(data); - return te_eval(solid_props->power, vtx); + return 0; /* Keep */ } -static void -release_solid_data(void* s) +/* Check probe position and move it to the closest point on an interface + * if on_interface is set */ +static res_T +check_probe_conform_to_type + (const struct stardis* stardis, + const int move2boundary, + double pos[3], + size_t* iprim, + double uv[2]) { - struct solid* solid = (struct solid*)s; - te_free(solid->t_init); - te_free(solid->power); -} + res_T res = RES_OK; + struct s3d_device* s3d = NULL; + struct s3d_scene* s3d_scn = NULL; + struct s3d_shape* s3d_shp = NULL; + struct s3d_scene_view* s3d_view = NULL; + struct s3d_hit hit = S3D_HIT_NULL; + struct s3d_vertex_data attribs; + struct filter_ctx filter_ctx = FILTER_CTX_DEFAULT__; + float origin[3]; + unsigned vcount, tcount, j; + + ASSERT(stardis && pos && iprim && uv); + + attribs.type = S3D_FLOAT3; + attribs.usage = S3D_POSITION; + attribs.get = sg3d_sXd_geometry_get_position; + + ERR(s3d_device_create(stardis->logger, stardis->allocator, 0, &s3d)); + ERR(s3d_scene_create(s3d, &s3d_scn)); + ERR(s3d_shape_create_mesh(s3d, &s3d_shp)); + + /* Back to s3d API type for ntris and nverts */ + ERR(sg3d_geometry_get_unique_triangles_count(stardis->geometry.sg3d, &tcount)); + ERR(sg3d_geometry_get_unique_vertices_count(stardis->geometry.sg3d, &vcount)); + ERR(s3d_mesh_setup_indexed_vertices(s3d_shp, + tcount, sg3d_sXd_geometry_get_indices, + vcount, &attribs, 1, stardis->geometry.sg3d)); + /* Need a filter to sort out some tricky configurations (see filter comments) */ + ERR(s3d_mesh_set_hit_filter_function(s3d_shp, hit_filter, NULL)); + ERR(s3d_scene_attach_shape(s3d_scn, s3d_shp)); + ERR(s3d_scene_view_create(s3d_scn, S3D_TRACE, &s3d_view)); + + S3D(device_ref_put(s3d)); s3d = NULL; + S3D(scene_ref_put(s3d_scn)); s3d_scn = NULL; + S3D(shape_ref_put(s3d_shp)); s3d_shp = NULL; + + f3_set_d3(origin, pos); + filter_ctx.stardis = stardis; + ERR(s3d_scene_view_closest_point(s3d_view, origin, FLT_MAX, &filter_ctx, &hit)); + S3D(scene_view_ref_put(s3d_view)); s3d_view = NULL; + if(S3D_HIT_NONE(&hit)) { + res = RES_BAD_ARG; + goto error; + } + ASSERT(filter_ctx.dist == hit.distance); + + if(move2boundary) { + /* Need to move probe to closest boundary */ + int danger = 0; + if(filter_ctx.outside) { + /* Not an error as probe moved to boundary */ + logger_print(stardis->logger, LOG_WARNING, + "Probe was outside the model.\n"); + } + if(!filter_ctx.desc) { + /* Not an error as probe moved to boundary */ + if(!filter_ctx.probe_on_boundary && !filter_ctx.outside) + logger_print(stardis->logger, LOG_WARNING, + "Could not determine the medium probe is in.\n"); + } else { + if(filter_ctx.desc->type == DESC_MAT_SOLID) { + double delta = filter_ctx.desc->d.solid.delta; + ASSERT(delta < INF); + logger_print(stardis->logger, LOG_OUTPUT, + "Probe was in solid '%s'.\n", str_cget(&filter_ctx.desc->d.solid.name)); + if(filter_ctx.dist > 2 * delta) { + logger_print(stardis->logger, LOG_ERROR, + "Probe moved to (%g, %g, %g), primitive %u, uv = (%g, %g).\n", + SPLIT3(filter_ctx.pos), hit.prim.prim_id, SPLIT2(hit.uv)); + logger_print(stardis->logger, LOG_ERROR, + "Move is %g delta long. Use -p instead of -P.\n", + filter_ctx.dist / delta); + res = RES_BAD_ARG; + goto error; + } + if(filter_ctx.dist > 0.5 * delta) { + logger_print(stardis->logger, LOG_WARNING, + "Probe was %g delta from closest boundary. " + "Consider using -P instead of -p.\n", + filter_ctx.dist / delta); + } else { + if(filter_ctx.dist != 0) + logger_print(stardis->logger, LOG_OUTPUT, + "Probe was %g delta from closest boundary.\n", + filter_ctx.dist / delta); + } + } else { + /* TODO: check move length wrt local geometry? */ + logger_print(stardis->logger, LOG_OUTPUT, + "Probe was in fluid '%s'.\n", str_cget(&filter_ctx.desc->d.fluid.name)); + logger_print(stardis->logger, LOG_OUTPUT, + "Probe distance from closest boundary was %g.\n", filter_ctx.dist); + } + } + /* Check if moved to vertex/edge */ + FOR_EACH(j, 0, 2) { + if(CLAMP(hit.uv[j], 0.0005, 0.9995) != hit.uv[j]) + danger++; + } + if(danger) { + logger_print(stardis->logger, LOG_WARNING, + "Probe %s close to / on %s. " + "If computation fails, try moving it slightly.\n", + (filter_ctx.dist != 0 ? "moved" : "is"), (danger == 1 ? "an edge" : "a vertex")); + } + if(filter_ctx.probe_on_boundary) { + logger_print(stardis->logger, LOG_OUTPUT, + "Probe is on primitive %u, uv = (%g, %g), not moved.\n", + hit.prim.prim_id, SPLIT2(hit.uv)); + } else { + logger_print(stardis->logger, LOG_OUTPUT, + "Probe moved to (%g, %g, %g), primitive %u, uv = (%g, %g).\n", + SPLIT3(filter_ctx.pos), hit.prim.prim_id, SPLIT2(hit.uv)); + } + d3_set_f3(pos, filter_ctx.pos); + } else { + /* Need to check medium */ + if(filter_ctx.outside) { + logger_print(stardis->logger, LOG_ERROR, + "Probe is outside the model.\n"); + logger_print(stardis->logger, LOG_ERROR, + "Closest geometry is primitive %u, uv = (%g, %g), pos (%g, %g, %g).\n", + hit.prim.prim_id, SPLIT2(hit.uv), SPLIT3(filter_ctx.pos)); + res = RES_BAD_ARG; + goto error; + } + if(filter_ctx.probe_on_boundary) { + logger_print(stardis->logger, LOG_ERROR, + "Probe is on primitive %u, uv = (%g, %g). Use -P instead of -p.\n", + hit.prim.prim_id, SPLIT2(hit.uv)); + res = RES_BAD_ARG; + goto error; + } + if(!filter_ctx.desc) { + logger_print(stardis->logger, LOG_ERROR, + "Could not determine the medium probe is in. " + "Try moving it slightly.\n"); + logger_print(stardis->logger, LOG_ERROR, + "Closest geometry is primitive %u, uv = (%g, %g), distance %g.\n", + hit.prim.prim_id, SPLIT2(hit.uv), filter_ctx.dist); + res = RES_BAD_ARG; + goto error; + } + logger_print(stardis->logger, LOG_OUTPUT, + "Probe is in solid '%s'.\n", str_cget(&filter_ctx.desc->d.solid.name)); + if(filter_ctx.desc->type == DESC_MAT_SOLID) { + double delta = filter_ctx.desc->d.solid.delta; + if(filter_ctx.dist < 0.25 * delta) { + logger_print(stardis->logger, LOG_ERROR, + "Probe is %g delta from closest boundary. Use -P instead of -p.\n", + filter_ctx.dist / delta); + logger_print(stardis->logger, LOG_ERROR, + "Closest geometry is primitive %u, uv = (%g, %g).\n", + hit.prim.prim_id, SPLIT2(hit.uv)); + res = RES_BAD_ARG; + goto error; + } + if(filter_ctx.dist < 0.5 * delta) { + logger_print(stardis->logger, LOG_WARNING, + "Probe is %g delta from closest boundary. " + "Consider using -P instead of -p.\n", + filter_ctx.dist / delta); + } else { + logger_print(stardis->logger, LOG_OUTPUT, + "Probe is %g delta from closest boundary.\n", + filter_ctx.dist / delta); + } + } else { + logger_print(stardis->logger, LOG_WARNING, + "Probe is in fluid '%s': computing fluid temperature, " + "not using a specific position.\n", + str_cget(&filter_ctx.desc->d.fluid.name)); + /* In fluid; TODO: check distance wrt local geometry (use 4V/S?) */ + } + } -/******************************************************************************* - * Interface data - ******************************************************************************/ -struct intface { - double hc; /* Convection coefficient */ - double emissivity; - double alpha; - /* TinyExpr stuff to compute temperature & flux */ - struct te_expr *temperature; - struct te_expr *flux; - /* IDs */ - unsigned front_boundary_id, back_boundary_id; -}; + *iprim = hit.prim.prim_id; + d2_set_f2(uv, hit.uv); +end: + if(s3d) S3D(device_ref_put(s3d)); + if(s3d_scn) S3D(scene_ref_put(s3d_scn)); + if(s3d_shp) S3D(shape_ref_put(s3d_shp)); + if(s3d_view) S3D(scene_view_ref_put(s3d_view)); -static double -interface_get_convection_coef - (const struct sdis_interface_fragment* frag, struct sdis_data* data) -{ - const struct intface* interface_props = sdis_data_cget(data); - (void)frag; - return interface_props->hc; + return res; +error: + goto end; } -static double -interface_get_temperature - (const struct sdis_interface_fragment* frag, struct sdis_data* data) +static res_T +compute_probe(struct stardis* stardis) { - const struct intface* interface_props = sdis_data_cget(data); - - if (interface_props->temperature == NULL) - return -1; - - return te_eval(interface_props->temperature, frag); -} + res_T res = RES_OK; + double uv[2] = { 0,0 }; + size_t iprim = SIZE_MAX; + struct sdis_green_function* green = NULL; + struct sdis_estimator* estimator = NULL; + struct dump_path_context dump_ctx; + struct sdis_solve_probe_args args = SDIS_SOLVE_PROBE_ARGS_DEFAULT; + FILE* stream = NULL; -static double -interface_get_flux - (const struct sdis_interface_fragment* frag, struct sdis_data* data) -{ - const struct intface* interface_props = sdis_data_cget(data); + ASSERT(stardis && (stardis->mode & MODE_PROBE_COMPUTE)); - if (interface_props->flux == NULL) - return SDIS_FLUX_NONE; - - return te_eval(interface_props->flux, frag); -} + ERR(check_probe_conform_to_type(stardis, 0, stardis->probe, &iprim, uv)); -static void -release_interface_data(void* s) -{ - struct intface* intface = (struct intface*)s; - te_free(intface->temperature); - te_free(intface->flux); -} + args.nrealisations = stardis->samples; + d3_set(args.position, stardis->probe); + d2_set(args.time_range, stardis->time_range); -static double -interface_get_emissivity - (const struct sdis_interface_fragment* frag, struct sdis_data* data) -{ - const struct intface* interface_props = sdis_data_cget(data); - (void)frag; - return interface_props->emissivity; -} + if(stardis->mode & (MODE_BIN_GREEN | MODE_GREEN)) { + ERR(sdis_solve_probe_green_function(stardis->sdis_scn, &args, &green)); + if(stardis->mode & MODE_BIN_GREEN) { + ASSERT(!str_is_empty(&stardis->bin_green_filename)); + stream = fopen(str_cget(&stardis->bin_green_filename), "wb"); + if(!stream) { + res = RES_IO_ERR; + goto error; + } + ERR(dump_green_bin(green, stardis, stream)); + fclose(stream); stream = NULL; + if(str_cget(&stardis->end_paths_filename) + && strlen(str_cget(&stardis->end_paths_filename))) + { + stream = fopen(str_cget(&stardis->end_paths_filename), "w"); + if(!stream) { + res = RES_IO_ERR; + goto error; + } + ERR(dump_paths_end(green, stardis, stream)); + fclose(stream); stream = NULL; + } + } + if(stardis->mode & MODE_GREEN) { + ERR(dump_green_ascii(green, stardis, stdout)); + } + } else { + args.register_paths = stardis->dump_paths; + ERR(sdis_solve_probe(stardis->sdis_scn, &args, &estimator)); + ERR(print_single_MC_result(estimator, stardis, stdout)); + /* Dump paths recorded according to user settings */ + dump_ctx.stardis = stardis; + dump_ctx.rank = 0; + ERR(sdis_estimator_for_each_path(estimator, dump_path, &dump_ctx)); + } -static INLINE double -interface_get_alpha - (const struct sdis_interface_fragment* frag, struct sdis_data* data) -{ - const struct intface* interface_props = sdis_data_cget(data); - (void)frag; - return interface_props->alpha; +end: + if(stream) fclose(stream); + if(estimator) SDIS(estimator_ref_put(estimator)); + if(green) SDIS(green_function_ref_put(green)); + return res; +error: + goto end; } static res_T -select_probe_type - (const struct sdis_scene* scn, - const struct triangle* triangle, - const struct vertex* vertex, - const struct description* descriptions, - const int on_interface, - double pos[3], - size_t* iprim, - double uv[2]) +compute_probe_on_interface(struct stardis* stardis) { res_T res = RES_OK; - double min_d = DBL_MAX, min_delta = DBL_MAX, new_pos[3], tmp_uv[2], min_uv[2]; - const struct description *min_desc = NULL; - size_t i = 0, found = SIZE_MAX; - enum description_type min_type = DESCRIPTION_TYPE_COUNT__; - - for (i = 0; i < sa_size(triangle); ++i) { - const struct triangle* tr = triangle + i; - double dp[3], d, projected_pos[3], nn[3]; - float v0[3], v1[3], v2[3], e1[3], e2[3], n[3]; - const struct description *desc = NULL; - enum sdis_side s; - - res = sdis_scene_boundary_project_position(scn, i, pos, tmp_uv); - if (res != RES_OK) return res; - res = sdis_scene_get_boundary_position(scn, i, tmp_uv, projected_pos); - if (res != RES_OK) return res; - d3_sub(dp, projected_pos, pos); - d = d3_len(dp); - - if (d == 0) { - /* Best possible match */ - found = i; - fprintf(stderr, - "The probe is on the primitive %llu.\n", (long long int)*iprim); - break; - } - if (d >= min_d) { - /* No improvement */ - continue; - } - min_d = d; - d2_set(min_uv, tmp_uv); - - /* Find side */ - f3_set(v0, vertex[tr->indices.data[0]].xyz); - f3_set(v1, vertex[tr->indices.data[1]].xyz); - f3_set(v2, vertex[tr->indices.data[2]].xyz); - f3_sub(e1, v1, v0); - f3_sub(e2, v2, v0); - f3_cross(n, e1, e2); - - /* Stardis solver convention is triangle normal is back side */ - d3_set_f3(nn, n); - s = (d3_dot(nn, dp) < 0) ? SDIS_BACK : SDIS_FRONT; - - if (s == SDIS_FRONT) { - if (tr->front_description != UINT_MAX) { - desc = descriptions + tr->front_description; + double uv[2] = { 0,0 }; + size_t iprim = SIZE_MAX; + struct sdis_estimator* estimator = NULL; + struct sdis_green_function* green = NULL; + struct dump_path_context dump_ctx; + struct sdis_solve_probe_boundary_args args + = SDIS_SOLVE_PROBE_BOUNDARY_ARGS_DEFAULT; + FILE* stream = NULL; + + ASSERT(stardis && (stardis->mode & MODE_PROBE_COMPUTE_ON_INTERFACE)); + ERR(check_probe_conform_to_type(stardis, 1, stardis->probe, &iprim, uv)); + ASSERT(iprim != SIZE_MAX); + + args.nrealisations = stardis->samples; + args.iprim = iprim; + d3_set(args.uv, uv); + args.side = SDIS_FRONT; + d2_set(args.time_range, stardis->time_range); + + if(stardis->mode & (MODE_BIN_GREEN | MODE_GREEN)) { + ERR(sdis_solve_probe_boundary_green_function(stardis->sdis_scn, &args, + &green)); + if(stardis->mode & MODE_BIN_GREEN) { + ASSERT(!str_is_empty(&stardis->bin_green_filename)); + stream = fopen(str_cget(&stardis->bin_green_filename), "wb"); + if(!stream) { + res = RES_IO_ERR; + goto error; } - } else { - if (tr->back_description != UINT_MAX) { - desc = descriptions + tr->back_description; + ERR(dump_green_bin(green, stardis, stream)); + fclose(stream); stream = NULL; + if(str_cget(&stardis->end_paths_filename) + && strlen(str_cget(&stardis->end_paths_filename))) + { + stream = fopen(str_cget(&stardis->end_paths_filename), "w"); + if(!stream) { + res = RES_IO_ERR; + goto error; + } + ERR(dump_paths_end(green, stardis, stream)); + fclose(stream); stream = NULL; } } - - min_desc = desc; - if (!desc) continue; - - min_type = desc->type; - if (desc->type == DESC_MAT_SOLID) { - found = i; - min_delta = desc->d.solid.delta; - d3_set(new_pos, projected_pos); + if(stardis->mode & MODE_GREEN) { + ERR(dump_green_ascii(green, stardis, stdout)); } - } - - if (!min_desc) { - /* Don't manage pos is outside the model - * but very close and could be considered on the boundary */ - fprintf(stderr, "The probe is outside the model.\n"); - return RES_BAD_ARG; - } - - if (on_interface) { - *iprim = found; - if (min_type == DESC_MAT_FLUID) - fprintf(stderr, "Probe was on a fluid.\n"); - fprintf(stderr, "The probe is moved to %g,%g,%g (primitive %llu)\n", - SPLIT3(new_pos), (long long int)*iprim); - if (min_type == DESC_MAT_SOLID && min_d > min_delta) - fprintf(stderr, "WARNING: The probe move is %.1f delta long.\n", min_d / min_delta); - d3_set(pos, new_pos); - d2_set(uv, min_uv); } else { - if (min_type == DESC_MAT_FLUID) - fprintf(stderr, - "The probe is in a fluid: computing fluid temperature, not using a specific position.\n"); - if (min_type == DESC_MAT_SOLID && min_d < min_delta) { - fprintf(stderr, - "WARNING: The probe is very close to the primitive %llu (%.1f delta).\n", - (long long int)found, min_d / min_delta); - fprintf(stderr, - "To have this probe moved on the closest interface use -P instead of -p\n"); - } + args.register_paths = stardis->dump_paths; + ERR(sdis_solve_probe_boundary(stardis->sdis_scn, &args, &estimator)); + ERR(print_single_MC_result(estimator, stardis, stdout)); + /* Dump paths recorded according to user settings */ + dump_ctx.stardis = stardis; + dump_ctx.rank = 0; + ERR(sdis_estimator_for_each_path(estimator, dump_path, &dump_ctx)); } +end: + if(stream) fclose(stream); + if(estimator) SDIS(estimator_ref_put(estimator)); + if(green) SDIS(green_function_ref_put(green)); return res; +error: + goto end; } -static void -dump_image(const struct sdis_estimator_buffer* buf) +static res_T +auto_look_at + (struct sdis_scene* scn, + const double fov_x, /* Horizontal field of view in radian */ + const double proj_ratio, /* Width / height */ + const double up[3], /* Up vector */ + double position[3], + double target[3]) { - size_t definition[2]; - double* temps = NULL; - size_t ix, iy; - - CHK(buf != NULL); - CHK(sdis_estimator_buffer_get_definition(buf, definition) == RES_OK); - - temps = mem_alloc(definition[0] * definition[1] *sizeof(double)); - CHK(temps != NULL); - - /* Compute the per pixel temperature */ - fprintf(stdout,"# vtk DataFile Version 2.0\nvtk output\nASCII\nDATASET STRUCTURED_POINTS\n"); - fprintf(stdout,"DIMENSIONS %zu %zu 1\n", definition[0], definition[1]); - fprintf(stdout,"ORIGIN 0 0 0\n"); - fprintf(stdout,"SPACING 1 1 1\n"); - fprintf(stdout,"POINT_DATA %zu\n", definition[0] * definition[1]); - fprintf(stdout,"SCALARS temperature_estimate float 1\n"); - fprintf(stdout,"LOOKUP_TABLE default\n"); - FOR_EACH(iy, 0, definition[1]) { - FOR_EACH(ix, 0, definition[0]) { - const struct sdis_estimator* estimator; - struct sdis_mc T; - CHK(sdis_estimator_buffer_at(buf, ix, iy, &estimator) == RES_OK); - CHK(sdis_estimator_get_temperature(estimator, &T) == RES_OK); - fprintf(stdout,"%f\n", T.E); - } - } - fprintf(stdout, "SCALARS temperature_std_dev float 1\n"); - fprintf(stdout, "LOOKUP_TABLE default\n"); - FOR_EACH(iy, 0, definition[1]) { - FOR_EACH(ix, 0, definition[0]) { - const struct sdis_estimator* estimator; - struct sdis_mc T; - CHK(sdis_estimator_buffer_at(buf, ix, iy, &estimator) == RES_OK); - CHK(sdis_estimator_get_temperature(estimator, &T) == RES_OK); - fprintf(stdout, "%f\n", T.SE); - } - } - fprintf(stdout, "SCALARS computation_time float 1\n"); - fprintf(stdout, "LOOKUP_TABLE default\n"); - FOR_EACH(iy, 0, definition[1]) { - FOR_EACH(ix, 0, definition[0]) { - const struct sdis_estimator* estimator; - struct sdis_mc time; - CHK(sdis_estimator_buffer_at(buf, ix, iy, &estimator) == RES_OK); - CHK(sdis_estimator_get_realisation_time(estimator, &time) == RES_OK); - fprintf(stdout, "%f\n", time.E); - } + double lower[3], upper[3]; + double up_abs[3]; + double axis_min[3]; + double axis_x[3]; + double axis_z[3]; + double tmp[3]; + double radius; + double depth; + res_T res; + ASSERT(scn && fov_x && proj_ratio && up); + + ERR(sdis_scene_get_aabb(scn, lower, upper)); + + if(lower[0] > upper[0] || lower[1] > upper[1] || lower[2] > upper[2]) { + /* Empty scene */ + d3(position, STARDIS_DEFAULT_RENDERING_POS); + d3(target, STARDIS_DEFAULT_RENDERING_TGT); + goto exit; } - fprintf(stdout, "SCALARS computation_time_std_dev float 1\n"); - fprintf(stdout, "LOOKUP_TABLE default\n"); - FOR_EACH(iy, 0, definition[1]) { - FOR_EACH(ix, 0, definition[0]) { - const struct sdis_estimator* estimator; - struct sdis_mc time; - CHK(sdis_estimator_buffer_at(buf, ix, iy, &estimator) == RES_OK); - CHK(sdis_estimator_get_realisation_time(estimator, &time) == RES_OK); - fprintf(stdout, "%f\n", time.SE); - } + + /* The target is the scene centroid */ + d3_muld(target, d3_add(target, lower, upper), 0.5); + + /* Define which up dimension is minimal and use its unit vector to compute a + * vector orthogonal to `up'. This ensures that the unit vector and `up' are + * not collinear so that their cross product is not a zero vector. */ + up_abs[0] = fabs(up[0]); + up_abs[1] = fabs(up[1]); + up_abs[2] = fabs(up[2]); + if(up_abs[0] < up_abs[1]) { + if(up_abs[0] < up_abs[2]) d3(axis_min, 1, 0, 0); + else d3(axis_min, 0, 0, 1); + } else { + if(up_abs[1] < up_abs[2]) d3(axis_min, 0, 1, 0); + else d3(axis_min, 0, 0, 1); } - fprintf(stdout, "SCALARS temperature_failures_count int 1\n"); - fprintf(stdout, "LOOKUP_TABLE default\n"); - FOR_EACH(iy, 0, definition[1]) { - FOR_EACH(ix, 0, definition[0]) { - const struct sdis_estimator* estimator; - size_t nfails; - CHK(sdis_estimator_buffer_at(buf, ix, iy, &estimator) == RES_OK); - CHK(sdis_estimator_get_failure_count(estimator, &nfails) == RES_OK); - fprintf(stdout, "%zu\n", nfails); - } + d3_normalize(axis_x, d3_cross(axis_x, up, axis_min)); + d3_normalize(axis_z, d3_cross(axis_z, up, axis_x)); + + /* Find whether the XYZ or the ZYX basis maximise the model's visibility */ + if(fabs(d3_dot(axis_x, upper)) < fabs(d3_dot(axis_z, upper))) { + SWAP(double, axis_x[0], axis_z[0]); + SWAP(double, axis_x[1], axis_z[1]); + SWAP(double, axis_x[2], axis_z[2]); } - mem_rm(temps); -} - -/******************************************************************************* - * - ******************************************************************************/ + /* Ensure that the whole model is visible */ + radius = d3_len(d3_sub(tmp, upper, lower)) * 0.5; + if(proj_ratio < 1) { + depth = radius / sin(fov_x / 2.0); + } else { + depth = radius / sin(fov_x / (2.0 * proj_ratio)); + } -struct int_descs { - unsigned front, back, connect; -}; -#define INT_DESCS_NULL__ { UINT_MAX, UINT_MAX, UINT_MAX } -static const struct int_descs INT_DESCS_NULL = INT_DESCS_NULL__; + /* Define the camera position */ + d3_sub(position, target, d3_muld(tmp, axis_z, depth)); -static INLINE char -eq_desc(const struct int_descs* a, const struct int_descs* b) -{ - return (char)(a->front == b->front && a->back == b->back - && a->connect == b->connect); -} + /* Empirically move the position to find a better point of view */ + d3_add(position, position, d3_muld(tmp, up, radius)); /*Empirical offset*/ + d3_add(position, position, d3_muld(tmp, axis_x, radius)); /*Empirical offset*/ + d3_normalize(tmp, d3_sub(tmp, target, position)); + d3_sub(position, target, d3_muld(tmp, tmp, depth)); -static INLINE size_t -hash_desc(struct int_descs const* key) -{ -#ifdef ARCH_32BITS - return (size_t)hash_fnv32(key, 3 * sizeof(unsigned)); -#elif defined(ARCH_64BITS) - return (size_t)hash_fnv64(key, 3 * sizeof(unsigned)); -#else -#error "Unexpected architecture" -#endif +exit: + return res; +error: + goto exit; } -#include <rsys/hash_table.h> -/* Declare the hash table that map a vertex to its index */ -#define HTABLE_NAME intface -#define HTABLE_DATA struct sdis_interface* -#define HTABLE_KEY struct int_descs -#define HTABLE_KEY_FUNCTOR_EQ eq_desc -#define HTABLE_KEY_FUNCTOR_HASH hash_desc -#include <rsys/hash_table.h> - static res_T -create_fluid - (struct sdis_device* dev, - const char* name, - const double rho, - const double cp, - const int is_green, - const int is_outside, - const char* tinit_expr, - const char* t_expr, - struct sdis_medium*** media_ptr, - unsigned* out_id) +compute_camera(struct stardis* stardis) { res_T res = RES_OK; - struct sdis_fluid_shader fluid_shader = SDIS_FLUID_SHADER_NULL; - struct sdis_data* data = NULL; - struct fluid* fluid_props; - size_t sz; - /* Could be less restrictive if green output included positions/dates */ - int prohibited = is_green ? NO_VAR_ALLOWED : ALL_VARS_ALLOWED; - - ASSERT(dev && rho >= 0 && cp >= 0 && media_ptr && out_id); - fluid_shader.calorific_capacity = fluid_get_calorific_capacity; - fluid_shader.volumic_mass = fluid_get_volumic_mass; - fluid_shader.temperature = fluid_get_temperature; - res = sdis_data_create(dev, sizeof(struct fluid), ALIGNOF(struct fluid), - release_fluid_data, &data); - if (res != RES_OK) goto error; - - sz = sa_size(*media_ptr); - ASSERT(sz < INT_MAX); - fluid_props = sdis_data_get(data); /* Fetch the allocated memory space */ - if (name) strncpy(fluid_props->name, name, sizeof(fluid_props->name)); - else fluid_props->name[0] = '\0'; - fluid_props->cp = cp; - fluid_props->rho = rho; - fluid_props->t_init = NULL; - fluid_props->temp = NULL; - fluid_props->is_green = is_green; - fluid_props->is_outside = is_outside; - fluid_props->t0 = 0; - fluid_props->id = (unsigned)sz; - - if (tinit_expr) { - res = compile_expr_to_fn(&fluid_props->t_init, tinit_expr, - prohibited | T_PROHIBITED, NULL); - if (res != RES_OK) { - fprintf(stderr, "Invalid initial temperature expression: %s\n", - tinit_expr); - goto error; - } + double proj_ratio; + size_t width, height; + struct sdis_estimator_buffer* buf = NULL; + struct sdis_solve_camera_args args = SDIS_SOLVE_CAMERA_ARGS_DEFAULT; + struct sdis_camera* cam = NULL; + struct dump_path_context dump_ctx; + size_t definition[2]; + size_t ix, iy; + + ASSERT(stardis + && !(stardis->mode & (MODE_BIN_GREEN | MODE_GREEN)) + && (stardis->mode & MODE_IR_COMPUTE)); + + width = (size_t)stardis->camera.img_width; + height = (size_t)stardis->camera.img_height; + proj_ratio = (double)width / (double)height; + /* Setup the camera */ + ERR(sdis_camera_create(stardis->dev, &cam)); + ERR(sdis_camera_set_proj_ratio(cam, proj_ratio)); + ERR(sdis_camera_set_fov(cam, MDEG2RAD(stardis->camera.fov))); + if(stardis->camera.auto_look_at) { + ERR(auto_look_at(stardis->sdis_scn, MDEG2RAD(stardis->camera.fov), proj_ratio, + stardis->camera.up, stardis->camera.pos, stardis->camera.tgt)); + logger_print(stardis->logger, LOG_OUTPUT, + "Camera auto-look at: position=%g,%g,%g target=%g,%g,%g.\n", + SPLIT3(stardis->camera.pos), SPLIT3(stardis->camera.tgt)); } - if (t_expr) { - res = compile_expr_to_fn(&fluid_props->temp, t_expr, prohibited, NULL); - if (res != RES_OK) { - fprintf(stderr, "Invalid temperature expression: %s\n", - t_expr); - goto error; + ERR(sdis_camera_look_at(cam, + stardis->camera.pos, + stardis->camera.tgt, + stardis->camera.up)); + + args.cam = cam; + d2_set(args.time_range, stardis->camera.time_range); + args.image_resolution[0] = width; + args.image_resolution[1] = height; + args.spp = (size_t)stardis->camera.spp; + args.register_paths = stardis->dump_paths; + + /* Launch the simulation */ + ERR(sdis_solve_camera(stardis->sdis_scn, &args, &buf)); + + /* Write the image */ + ASSERT(stardis->camera.fmt == STARDIS_RENDERING_OUTPUT_FILE_FMT_VTK + || stardis->camera.fmt == STARDIS_RENDERING_OUTPUT_FILE_FMT_HT); + if(stardis->camera.fmt == STARDIS_RENDERING_OUTPUT_FILE_FMT_VTK) + ERR(dump_vtk_image(buf, stdout)); + else ERR(dump_ht_image(buf, stdout)); + + /* Dump paths recorded according to user settings */ + dump_ctx.stardis = stardis; + dump_ctx.rank = 0; + ERR(sdis_estimator_buffer_get_definition(buf, definition)); + FOR_EACH(iy, 0, definition[1]) { + FOR_EACH(ix, 0, definition[0]) { + const struct sdis_estimator* estimator; + ERR(sdis_estimator_buffer_at(buf, ix, iy, &estimator)); + ERR(sdis_estimator_for_each_path(estimator, dump_path, &dump_ctx)); } } - res = sdis_fluid_create(dev, &fluid_shader, data, sa_add(*media_ptr, 1)); - if (res != RES_OK) goto error; - *out_id = fluid_props->id; end: - if (data) SDIS(data_ref_put(data)); + if(cam) SDIS(camera_ref_put(cam)); + if(buf) SDIS(estimator_buffer_ref_put(buf)); return res; error: goto end; } static res_T -create_solid - (struct sdis_device* dev, - const char* name, - const double lambda, - const double rho, - const double cp, - const double delta, - const int is_green, - const int is_outside, - const char* tinit_expr, /* Can be NULL if not used by getter */ - const char* t_expr, /* Can be NULL if not used by getter */ - const char* power_expr, /* Can be NULL */ - struct sdis_medium*** media_ptr, - int* out_has_power, /* Can be NULL */ - unsigned* out_id) +compute_medium(struct stardis* stardis) { res_T res = RES_OK; - struct sdis_solid_shader solid_shader = SDIS_SOLID_SHADER_NULL; - struct sdis_data* data = NULL; - struct solid* solid_props; - size_t sz; - /* Could be less restrictive if green output included positions/dates */ - int prohibited = is_green ? NO_VAR_ALLOWED : ALL_VARS_ALLOWED; - - ASSERT(dev && lambda >= 0 && rho >= 0 && cp >= 0 && delta > 0 - && media_ptr && out_id); - solid_shader.calorific_capacity = solid_get_calorific_capacity; - solid_shader.thermal_conductivity = solid_get_thermal_conductivity; - solid_shader.volumic_mass = solid_get_volumic_mass; - solid_shader.delta_solid = solid_get_delta; -#if Stardis_VERSION_MINOR == 3 - solid_shader.delta_boundary = solid_get_delta_boundary; -#endif - solid_shader.temperature = solid_get_temperature; - res = sdis_data_create(dev, sizeof(struct solid), ALIGNOF(struct solid), - release_solid_data, &data); - if (res != RES_OK) goto error; - - sz = sa_size(*media_ptr); - ASSERT(sz < INT_MAX); - solid_props = sdis_data_get(data); /* Fetch the allocated memory space */ - if (name) strncpy(solid_props->name, name, sizeof(solid_props->name)); - else solid_props->name[0] = '\0'; - solid_props->lambda = lambda; - solid_props->rho = rho; - solid_props->cp = cp; - solid_props->delta = delta; - solid_props->t_init = NULL; - solid_props->temp = NULL; - solid_props->power = NULL; - solid_props->is_green = is_green; - solid_props->is_outside = is_outside; - solid_props->t0 = 0; - solid_props->id = (unsigned)sz; - if (tinit_expr) { - res = compile_expr_to_fn(&solid_props->t_init, tinit_expr, - prohibited | T_PROHIBITED, NULL); - if (res != RES_OK) { - fprintf(stderr, "Invalid initial temperature expression: %s\n", - tinit_expr); - goto error; - } + struct sdis_medium* medium = NULL; + struct sdis_estimator* estimator = NULL; + struct sdis_green_function* green = NULL; + struct sdis_solve_medium_args args = SDIS_SOLVE_MEDIUM_ARGS_DEFAULT; + struct dump_path_context dump_ctx; + FILE* stream = NULL; + + ASSERT(stardis && (stardis->mode & MODE_MEDIUM_COMPUTE)); + + /* Find medium */ + medium = find_medium_by_name(stardis, &stardis->solve_name, NULL); + if(medium == NULL) { /* Not found */ + logger_print(stardis->logger, LOG_ERROR, + "Cannot solve medium '%s' (unknown medium)\n", + str_cget(&stardis->solve_name)); + res = RES_BAD_ARG; + goto error; } - if (t_expr) { - res = compile_expr_to_fn(&solid_props->temp, t_expr, prohibited, NULL); - if (res != RES_OK) { - fprintf(stderr, "Invalid temperature expression: %s\n", - t_expr); - goto error; + + args.nrealisations = stardis->samples; + args.medium = medium; + d2_set(args.time_range, stardis->time_range); + + if(stardis->mode & (MODE_BIN_GREEN | MODE_GREEN)) { + ERR(sdis_solve_medium_green_function(stardis->sdis_scn, &args, &green)); + if(stardis->mode & MODE_BIN_GREEN) { + ASSERT(!str_is_empty(&stardis->bin_green_filename)); + stream = fopen(str_cget(&stardis->bin_green_filename), "wb"); + if(!stream) { + res = RES_IO_ERR; + goto error; + } + ERR(dump_green_bin(green, stardis, stream)); + fclose(stream); stream = NULL; + if(str_cget(&stardis->end_paths_filename) + && strlen(str_cget(&stardis->end_paths_filename))) + { + stream = fopen(str_cget(&stardis->end_paths_filename), "w"); + if(!stream) { + res = RES_IO_ERR; + goto error; + } + ERR(dump_paths_end(green, stardis, stream)); + fclose(stream); stream = NULL; + } } - } - if (power_expr) { - int has_power; - res = compile_expr_to_fn(&solid_props->power, power_expr, prohibited, &has_power); - if (res != RES_OK) { - fprintf(stderr, "Invalid volumic power expression: %s\n", - power_expr); - goto error; + if(stardis->mode & MODE_GREEN) { + ERR(dump_green_ascii(green, stardis, stdout)); } - if (out_has_power) *out_has_power = has_power; - if(has_power) solid_shader.volumic_power = solid_get_power; - } - else { - if (out_has_power) *out_has_power = 0; + } else { + args.register_paths = stardis->dump_paths; + ERR(sdis_solve_medium(stardis->sdis_scn, &args, &estimator)); + ERR(print_single_MC_result(estimator, stardis, stdout)); + /* Dump paths recorded according to user settings */ + dump_ctx.stardis = stardis; + dump_ctx.rank = 0; + ERR(sdis_estimator_for_each_path(estimator, dump_path, &dump_ctx)); } - res = sdis_solid_create(dev, &solid_shader, data, sa_add(*media_ptr, 1)); - if (res != RES_OK) goto error; - *out_id = solid_props->id; end: - if (data) SDIS(data_ref_put(data)); + if(stream) fclose(stream); + if(estimator) SDIS(estimator_ref_put(estimator)); + if(green) SDIS(green_function_ref_put(green)); return res; error: goto end; } static res_T -create_edge_file_if - (struct sdis_scene* scn, - struct mem_allocator* allocator) +add_compute_surface_triangle + (const unsigned unique_id, const unsigned itri, void* context) { res_T res = RES_OK; - unsigned i, comp, scount, vcount; - char name[64]; - FILE* obj = NULL; - struct htable_vrtx_rank ranks; - char structs_initialized = 0; - struct senc_descriptor* analyze = NULL; - struct darray_uint comps; - struct darray_uint edges; - unsigned cc_count = 0; - unsigned *cc, *ee, uimax = UINT_MAX; - - res = sdis_scene_get_analysis(scn, &analyze); - if (res != RES_OK) goto error; - res = sdis_scene_release_analysis(scn); - if (res != RES_OK) goto error; - - ASSERT(scn && allocator); - - res = senc_descriptor_get_frontier_segments_count(analyze, &scount); - if (res != RES_OK) goto error; - - if(scount == 0) - /* No edge to exit */ - goto end; - - fprintf(stderr, "Some frontier edges found: create OBJ files\n"); - - htable_vrtx_rank_init(allocator, &ranks); - darray_uint_init(allocator, &comps); - darray_uint_init(allocator, &edges); - structs_initialized = 1; - - /* Exit vertices, with a new numbering scheme */ - vcount = 0; - FOR_EACH(i, 0, scount) { - unsigned v, edge[2], rk[2]; - res = senc_descriptor_get_frontier_segment(analyze, i, edge); - if(res != RES_OK) goto error; - FOR_EACH(v, 0, 2) { - unsigned* p_rank; - p_rank = htable_vrtx_rank_find(&ranks, edge + v); - if(p_rank) { - rk[v] = *p_rank; - } else { - /* First appearance: allocate a rank and exit it */ - unsigned rank; - size_t tmp = htable_vrtx_rank_size_get(&ranks); - ASSERT(tmp < UINT_MAX); - rank = (unsigned) tmp; - res = htable_vrtx_rank_set(&ranks, edge + v, &rank); - if (res != RES_OK) goto error; - rk[v] = rank; - darray_uint_push_back(&comps, &uimax); - darray_uint_push_back(&edges, edge + v); - vcount++; - } - /* Manage components */ - cc = darray_uint_data_get(&comps); - if(cc[rk[0]] != UINT_MAX || cc[rk[1]] != UINT_MAX) { - /* If both components known, they must be the same */ - ASSERT(cc[rk[0]] == UINT_MAX || cc[rk[1]] == UINT_MAX - || cc[rk[0]] == cc[rk[1]]); - FOR_EACH(v, 0, 2) { - unsigned w = 1 - v; /* The other vertex */ - if (cc[rk[v]] == UINT_MAX) cc[rk[v]] = cc[rk[w]]; - } - } else { - /* None are in a know component: create a new one */ - cc[rk[0]] = cc[rk[1]] = cc_count++; - } - } - } - - /* Exit segments by component using the new numbering scheme */ - cc = darray_uint_data_get(&comps); - ee = darray_uint_data_get(&edges); - FOR_EACH(comp, 0, cc_count) { - unsigned v; - snprintf(name, sizeof(name), "frontier_component_%u.obj", comp); - obj = fopen(name, "w"); - if(!obj) goto error; - /* Exit all vertices even unused - * (same numbering scheme for all components) */ - FOR_EACH(v, 0, vcount) { - double coord[3]; - res = senc_descriptor_get_global_vertex(analyze, ee[v], coord); - if(res != RES_OK) goto error; - fprintf(obj, "v %f %f %f\n", SPLIT3(coord)); - } - FOR_EACH(i, 0, scount) { - unsigned edge[2], new_ranks[2]; - res = senc_descriptor_get_frontier_segment(analyze, i, edge); - if(res != RES_OK) goto error; - ASSERT(cc[edge[0]] == cc[edge[1]]); - if(cc[edge[0]] != comp) - /* Segment not in this component */ - continue; - FOR_EACH(v, 0, 2) { - unsigned* p_rank; - p_rank = htable_vrtx_rank_find(&ranks, edge + v); - ASSERT(p_rank && *p_rank < vcount); - new_ranks[v] = *p_rank; - } -#define OBJ_IDX(Rank) ((Rank)+1) - fprintf(obj, "l %u %u\n", OBJ_IDX(new_ranks[0]), OBJ_IDX(new_ranks[1])); -#undef OBJ_IDX - } - fclose(obj); obj = NULL; - } + const struct add_geom_ctx* ctx = context; + struct compute_surface* region; + ASSERT(ctx); (void)itri; + region = ctx->custom; + /* The triangle should be already in the model, but is not + * Keep it in err_triangles to allow dumps */ + ERR(darray_uint_push_back(&region->err_triangles, &unique_id)); end: - if(obj) fclose(obj); - if(analyze) senc_descriptor_ref_put(analyze); - if(structs_initialized) { - htable_vrtx_rank_release(&ranks); - darray_uint_release(&comps); - darray_uint_release(&edges); - } return res; error: goto end; } static res_T -dump_path - (const struct sdis_heat_path* path, - void* context) -{ - FILE* stream = context; - enum sdis_heat_path_flag status = SDIS_HEAT_PATH_NONE; - size_t i, vcount; - - /* Header */ - fprintf(stream, "---\n"); - fprintf(stream, "# vtk DataFile Version 2.0\n"); - fprintf(stream, "Heat paths\n"); - fprintf(stream, "ASCII\n"); - fprintf(stream, "DATASET POLYDATA\n"); - /* Write path positions */ - sdis_heat_path_get_vertices_count(path, &vcount); - fprintf(stream, "POINTS %lu double\n", (unsigned long)vcount); - FOR_EACH(i, 0, vcount) { - struct sdis_heat_vertex vtx; - CHK(sdis_heat_path_get_vertex(path, i, &vtx) == RES_OK); - fprintf(stream, "%g %g %g\n", SPLIT3(vtx.P)); - } - /* Write the segment of the path */ - fprintf(stream, "LINES %lu %lu\n", 1lu, (unsigned long)(1 + vcount)); - fprintf(stream, "%lu", (unsigned long)vcount); - FOR_EACH(i, 0, vcount) fprintf(stream, " %lu", (unsigned long)i); - fprintf(stream, "\n"); - fprintf(stream, "POINT_DATA %lu\n", (unsigned long)vcount); - /* Write the type of the random walk vertices */ - fprintf(stream, "SCALARS Vertex_Type float 1\n"); - fprintf(stream, "LOOKUP_TABLE vertex_type\n"); - FOR_EACH(i, 0, vcount) { - struct sdis_heat_vertex vtx; - CHK(sdis_heat_path_get_vertex(path, i, &vtx) == RES_OK); - switch (vtx.type) { - case SDIS_HEAT_VERTEX_CONDUCTION: fprintf(stream, "0.0\n"); break; - case SDIS_HEAT_VERTEX_CONVECTION: fprintf(stream, "0.5\n"); break; - case SDIS_HEAT_VERTEX_RADIATIVE: fprintf(stream, "1.0\n"); break; - default: FATAL("Unreachable code.\n"); break; - } - } - fprintf(stream, "LOOKUP_TABLE vertex_type 3\n"); - fprintf(stream, "0.0 1.0 1.0 1.0\n"); /* 0.0 = Magenta: conduction */ - fprintf(stream, "1.0 1.0 0.0 1.0\n"); /* 0.5 = Yellow: convection */ - fprintf(stream, "1.0 0.0 1.0 1.0\n"); /* 1.0 = Purple: radiative */ - /* Write the weights of the random walk vertices */ - fprintf(stream, "SCALARS Weight double 1\n"); - fprintf(stream, "LOOKUP_TABLE default\n"); - FOR_EACH(i, 0, vcount) { - struct sdis_heat_vertex vtx; - CHK(sdis_heat_path_get_vertex(path, i, &vtx) == RES_OK); - fprintf(stream, "%g\n", vtx.weight); - } - /* Write the time of the random walk vertices */ - fprintf(stream, "SCALARS Time double 1\n"); - fprintf(stream, "LOOKUP_TABLE default\n"); - FOR_EACH(i, 0, vcount) { - struct sdis_heat_vertex vtx; - CHK(sdis_heat_path_get_vertex(path, i, &vtx) == RES_OK); - fprintf(stream, "%g\n", IS_INF(vtx.time) ? FLT_MAX : vtx.time); - } - /* Write path type */ - fprintf(stream, "CELL_DATA %lu\n", 1lu); - fprintf(stream, "SCALARS Path_Type float 1\n"); - fprintf(stream, "LOOKUP_TABLE path_type\n"); - CHK(sdis_heat_path_get_status(path, &status) == RES_OK); - switch (status) { - case SDIS_HEAT_PATH_SUCCEED: fprintf(stream, "0.0\n"); break; - case SDIS_HEAT_PATH_FAILED: fprintf(stream, "1.0\n"); break; - default: FATAL("Unreachable code.\n"); break; - } - fprintf(stream, "LOOKUP_TABLE path_type 2\n"); - fprintf(stream, "0.0 0.0 1.0 1.0\n"); /* 0.0 = Blue: success */ - fprintf(stream, "1.0 0.0 0.0 1.0\n"); /* 1.0 = Red: failure */ - return RES_OK; -} - -static res_T -print_power_term - (struct sdis_medium* mdm, - const double power_term, - void* ctx) +merge_compute_surface_triangle + (const unsigned unique_id, + const unsigned itri, + const int reversed_triangle, + unsigned triangle_properties[SG3D_PROP_TYPES_COUNT__], + const unsigned merged_properties[SG3D_PROP_TYPES_COUNT__], + void* context, + int* merge_conflict_status) { - struct sdis_data* data = NULL; - enum sdis_medium_type type; - unsigned id; - struct w_ctx* w_ctx = ctx; - (void)w_ctx; - CHK(mdm && ctx); - - data = sdis_medium_get_data(mdm); - type = sdis_medium_get_type(mdm); - - switch (type) { - case SDIS_FLUID: { - FATAL("Unexpected power term in fluid"); -#if 0 - struct fluid* d__ = sdis_data_get(data); - id = d__->id; - desc = (struct description*)ctx + id; - ASSERT(id == desc->d.fluid.fluid_id); - printf("\tF\t%u\t%g", id, power_term); - break; -#endif - } - case SDIS_SOLID: { - struct solid* d__ = sdis_data_get(data); - id = d__->id; - ASSERT(id == w_ctx->desc[id].d.solid.solid_id); - printf("\tS\t%u\t%g", id, power_term); - break; - } - default: FATAL("Unreachable code.\n"); break; - } - return RES_OK; + res_T res = RES_OK; + const struct add_geom_ctx* ctx = context; + struct compute_surface* region; + size_t uid; + enum sdis_side side = reversed_triangle ? SDIS_BACK : SDIS_FRONT; + ASSERT(ctx); + (void)itri; (void)reversed_triangle; (void)triangle_properties; + (void)merged_properties; (void)merge_conflict_status; + region = ctx->custom; + /* Build the compute region */ + uid = unique_id; + ERR(darray_size_t_push_back(&region->primitives, &uid)); + ERR(darray_sides_push_back(&region->sides, &side)); +end: + return res; +error: + goto end; } static res_T -get_flux_terms - (struct sdis_interface* interf, - const enum sdis_side side, - const double flux_term, - void* ctx) +compute_boundary(struct stardis* stardis) { - struct sdis_data* data = NULL; - struct intface* d__; - unsigned id; - struct w_ctx* w_ctx = ctx; res_T res = RES_OK; - - CHK(interf && ctx); - - data = sdis_interface_get_data(interf); - d__ = sdis_data_get(data); - id = (side == SDIS_FRONT) ? d__->front_boundary_id : d__->back_boundary_id; - - switch (w_ctx->desc[id].type) { - case DESC_BOUND_T_FOR_SOLID: - case DESC_BOUND_T_FOR_FLUID: - case DESC_BOUND_H_FOR_SOLID: - case DESC_BOUND_H_FOR_FLUID: - FATAL("Cannot have a flux term here.\n"); break; - case DESC_BOUND_F_FOR_SOLID: { - double* w; - ASSERT(id == w_ctx->desc[id].d.f_boundary.mat_id); - w = htable_weigth_find(&w_ctx->weigths, &id); - if (w) - *w += flux_term; - else { - res = htable_weigth_set(&w_ctx->weigths, &id, &flux_term); + struct sdis_green_function* green = NULL; + struct sdis_estimator* estimator = NULL; + struct dump_path_context dump_ctx; + struct sdis_solve_boundary_args args = SDIS_SOLVE_BOUNDARY_ARGS_DEFAULT; + FILE* stream = NULL; + + ASSERT(stardis && (stardis->mode & MODE_BOUNDARY_COMPUTE)); + + args.nrealisations = stardis->samples; + args.primitives + = darray_size_t_cdata_get(&stardis->compute_surface.primitives); + args.sides = darray_sides_cdata_get(&stardis->compute_surface.sides); + args.nprimitives + = darray_size_t_size_get(&stardis->compute_surface.primitives); + d2_set(args.time_range, stardis->time_range); + + if(stardis->mode & (MODE_BIN_GREEN | MODE_GREEN)) { + ERR(sdis_solve_boundary_green_function(stardis->sdis_scn, &args, &green)); + if(stardis->mode & MODE_BIN_GREEN) { + ASSERT(!str_is_empty(&stardis->bin_green_filename)); + stream = fopen(str_cget(&stardis->bin_green_filename), "wb"); + if(!stream) { + res = RES_IO_ERR; + goto error; + } + ERR(dump_green_bin(green, stardis, stream)); + fclose(stream); stream = NULL; + if(str_cget(&stardis->end_paths_filename) + && strlen(str_cget(&stardis->end_paths_filename))) + { + stream = fopen(str_cget(&stardis->end_paths_filename), "w"); + if(!stream) { + res = RES_IO_ERR; + goto error; + } + ERR(dump_paths_end(green, stardis, stream)); + fclose(stream); stream = NULL; + } } - break; - } - default: FATAL("Unreachable code.\n"); break; + if(stardis->mode & MODE_GREEN) { + ERR(dump_green_ascii(green, stardis, stdout)); + } + } else { + args.register_paths = stardis->dump_paths; + ERR(sdis_solve_boundary(stardis->sdis_scn, &args, &estimator)); + ERR(print_single_MC_result(estimator, stardis, stdout)); + /* Dump paths recorded according to user settings */ + dump_ctx.stardis = stardis; + dump_ctx.rank = 0; + ERR(sdis_estimator_for_each_path(estimator, dump_path, &dump_ctx)); } + +end: + if(stream) fclose(stream); + if(estimator) SDIS(estimator_ref_put(estimator)); + if(green) SDIS(green_function_ref_put(green)); return res; +error: + goto end; } static res_T -print_sample - (struct sdis_green_path* path, - void* ctx) +compute_flux_boundary(struct stardis* stardis) { - struct sdis_point pt = SDIS_POINT_NULL; - struct sdis_data* data = NULL; - enum sdis_medium_type type; - struct htable_weigth_iterator it, end; - unsigned id; - size_t pcount, fcount; - struct w_ctx* w_ctx = ctx; res_T res = RES_OK; - CHK(path && ctx); - - res = sdis_green_path_get_limit_point(path, &pt); - if (res != RES_OK) goto error; - - - /* For each path, prints: - * # end #power_terms #flux_terms power_term_1 ... power_term_n flux_term_1 ... flux_term_n - * with: - * - end = end_type end_id; end_type = T | H | X | R | F | S - * - power_term_i = power_type_i power_id_i factor_i - * - flux_term_i = flux_id_i factor_i - */ - - switch (pt.type) { - case SDIS_FRAGMENT: { - struct intface* d__; - data = sdis_interface_get_data(pt.data.itfrag.intface); - d__ = sdis_data_get(data); - id = (pt.data.itfrag.fragment.side == SDIS_FRONT) - ? d__->front_boundary_id : d__->back_boundary_id; - ASSERT(id != UINT_MAX); - switch (w_ctx->desc[id].type) { - case DESC_BOUND_T_FOR_SOLID: - case DESC_BOUND_T_FOR_FLUID: - ASSERT(id == w_ctx->desc[id].d.t_boundary.mat_id); - printf("T\t%u", id); - break; - case DESC_BOUND_H_FOR_SOLID: - case DESC_BOUND_H_FOR_FLUID: - ASSERT(id == w_ctx->desc[id].d.h_boundary.mat_id); - printf("H\t%u", id); - break; - case DESC_BOUND_F_FOR_SOLID: - ASSERT(id == w_ctx->desc[id].d.f_boundary.mat_id); - printf("X\t%u", id); - break; - default: FATAL("Unreachable code.\n"); break; - } - break; - } - case SDIS_VERTEX: - type = sdis_medium_get_type(pt.data.mdmvert.medium); - data = sdis_medium_get_data(pt.data.mdmvert.medium); - if (pt.data.mdmvert.vertex.P[0] == INF) { - /* Radiative output */ - printf("R\t%u", (unsigned)sa_size(w_ctx->desc)); - } - else if (type == SDIS_FLUID) { - struct fluid* d__ = sdis_data_get(data); - id = d__->id; - ASSERT(id == w_ctx->desc[id].d.fluid.fluid_id); - if (d__->is_outside) - /* If outside the model and in a fluid with known temperature, - * its a fluid attached to a H boundary */ - printf("H\t%u", id); - /* In a standard fluid with known temperature */ - else printf("F\t%u", id); - - } else { - struct solid* d__ = sdis_data_get(data); - ASSERT(type == SDIS_SOLID); - id = d__->id; - ASSERT(id == w_ctx->desc[id].d.solid.solid_id); - ASSERT(!d__->is_outside); /* FIXME: what if in external solid? */ - printf("S\t%u", id); - } - break; - default: FATAL("Unreachable code.\n"); break; - } - - res = sdis_green_function_get_power_terms_count(path, &pcount); - if (res != RES_OK) goto error; + struct sdis_green_function* green = NULL; + struct sdis_estimator* estimator = NULL; + struct sdis_solve_boundary_flux_args args + = SDIS_SOLVE_BOUNDARY_FLUX_ARGS_DEFAULT; + struct dump_path_context dump_ctx; - htable_weigth_clear(&w_ctx->weigths); - res = sdis_green_path_for_each_flux_term(path, get_flux_terms, w_ctx); - if (res != RES_OK) goto error; - fcount = htable_weigth_size_get(&w_ctx->weigths); + ASSERT(stardis && (stardis->mode & MODE_FLUX_BOUNDARY_COMPUTE)); - printf("\t%zu\t%zu", pcount, fcount); + args.nrealisations = stardis->samples; + args.primitives + = darray_size_t_cdata_get(&stardis->compute_surface.primitives); + args.nprimitives + = darray_size_t_size_get(&stardis->compute_surface.primitives); + d2_set(args.time_range, stardis->time_range); - res = sdis_green_path_for_each_power_term(path, print_power_term, ctx); - if (res != RES_OK) goto error; + ERR(sdis_solve_boundary_flux(stardis->sdis_scn, &args, &estimator)); + ERR(print_single_MC_result(estimator, stardis, stdout)); - htable_weigth_begin(&w_ctx->weigths, &it); - htable_weigth_end(&w_ctx->weigths, &end); - while (!htable_weigth_iterator_eq(&it, &end)) { - double* w = htable_weigth_iterator_data_get(&it); - unsigned* k = htable_weigth_iterator_key_get(&it); - printf("\t%u\t%g", *k, *w); - htable_weigth_iterator_next(&it); - } - printf("\n"); + /* Dump paths recorded according to user settings */ + dump_ctx.stardis = stardis; + dump_ctx.rank = 0; + ERR(sdis_estimator_for_each_path(estimator, dump_path, &dump_ctx)); -error: +end: + if(estimator) SDIS(estimator_ref_put(estimator)); + if(green) SDIS(green_function_ref_put(green)); return res; +error: + goto end; } -res_T -stardis_compute(struct stardis* stardis, enum stardis_mode mode) +static res_T +compute_map(struct stardis* stardis) { res_T res = RES_OK; - struct sdis_device* dev = NULL; - struct sdis_medium** media = NULL; - struct sdis_interface** interfaces = NULL; - struct sdis_interface** interf_bytrg = NULL; - struct sdis_data* data = NULL; - - struct sdis_scene* scn = NULL; - struct sdis_estimator* estimator = NULL; - struct sdis_mc temperature; - struct sdis_estimator_buffer* buf = NULL; - struct sdis_camera* cam = NULL; - - struct sdis_medium* dummy_fluid = NULL; - unsigned dummy_fluid_id = UINT_MAX; - struct sdis_medium* dummy_solid = NULL; - unsigned dummy_solid_id = UINT_MAX; - - struct htable_intface htable_interfaces; - int htable_interfaces_initialised = 0; - double pos[3] = { 0,0,0 }; - double time[2] = { 0, 0 }; - size_t nfailures; - size_t smed_count = 0, fmed_count = 0, - tbound_count = 0, hbound_count = 0, fbound_count = 0, sfconnect_count = 0; - unsigned i = 0; - - res = sdis_device_create(NULL, &stardis->allocator, stardis->nthreads, 1, &dev); - if (res != RES_OK) goto error; - - /* Create media and property holders found in descriptions */ - for (i = 0; i < sa_size(stardis->descriptions); ++i) { - struct description* desc = stardis->descriptions + i; - /* Could be less restrictive if green output included positions/dates */ - int prohibited = (mode & GREEN_MODE) ? NO_VAR_ALLOWED : ALL_VARS_ALLOWED; - - switch (desc->type) { - case DESC_BOUND_H_FOR_SOLID: - hbound_count++; - /* Create an external fluid with bound temp as fluid temp */ - res = create_fluid(dev, NULL, 1, 1, (mode & GREEN_MODE), 1, NULL, - desc->d.h_boundary.T, &media, &desc->d.h_boundary.mat_id); - if (res != RES_OK) goto error; - break; - case DESC_BOUND_H_FOR_FLUID: - /* Create an external solid with bound temp as solid temp */ - hbound_count++; - res = create_solid(dev, NULL, INF, 1, 1, 1, (mode & GREEN_MODE), 1, NULL, - desc->d.h_boundary.T, NULL, &media, NULL, &desc->d.h_boundary.mat_id); - if (res != RES_OK) goto error; - break; - case DESC_BOUND_T_FOR_SOLID: - tbound_count++; - ASSERT(desc->d.t_boundary.T != NULL); - res = compile_expr_to_fn(&desc->d.t_boundary.te_temperature, - desc->d.t_boundary.T, prohibited, NULL); - if (res != RES_OK) { - fprintf(stderr, "Invalid boundary temperature expression: %s\n", - desc->d.t_boundary.T); - goto error; - } - if (dummy_fluid) { - /* Reuse external dummy fluid */ - desc->d.t_boundary.mat_id = dummy_fluid_id; - } else { - /* Create dummy fluid */ - res = create_fluid(dev, NULL, 1, 1, (mode & GREEN_MODE), 1, NULL, - NULL, &media, &desc->d.t_boundary.mat_id); - if (res != RES_OK) goto error; - dummy_fluid = sa_last(media); - dummy_fluid_id = desc->d.t_boundary.mat_id; - } - break; - case DESC_BOUND_T_FOR_FLUID: - tbound_count++; - ASSERT(desc->d.t_boundary.T != NULL); - res = compile_expr_to_fn(&desc->d.t_boundary.te_temperature, - desc->d.t_boundary.T, prohibited, NULL); - if (res != RES_OK) { - fprintf(stderr, "Invalid boundary temperature expression: %s\n", - desc->d.t_boundary.T); - goto error; - } - if (dummy_solid) { - /* Reuse external dummy solid */ - desc->d.t_boundary.mat_id = dummy_solid_id; - } else { - /* Create dummy solid */ - res = create_solid(dev, NULL, 1, 1, 1, 1, (mode & GREEN_MODE), 1, NULL, - NULL, NULL, &media, NULL, &desc->d.t_boundary.mat_id); - if (res != RES_OK) goto error; - dummy_solid = sa_last(media); - dummy_solid_id = desc->d.t_boundary.mat_id; - } - break; - case DESC_BOUND_F_FOR_SOLID: - fbound_count++; - ASSERT(desc->d.f_boundary.flux != NULL); - res = compile_expr_to_fn(&desc->d.f_boundary.te_flux, - desc->d.f_boundary.flux, 1, NULL); - if (res != RES_OK) { - fprintf(stderr, "Invalid boundary flux expression: %s\n", - desc->d.f_boundary.flux); - goto error; - } - if (dummy_fluid) { - /* Reuse external dummy fluid */ - desc->d.f_boundary.mat_id = dummy_fluid_id; - } else { - /* Create dummy fluid */ - res = create_fluid(dev, NULL, 1, 1, (mode & GREEN_MODE), 1, NULL, NULL, - &media, &desc->d.f_boundary.mat_id); - if (res != RES_OK) goto error; - dummy_fluid = sa_last(media); - dummy_fluid_id = desc->d.f_boundary.mat_id; - } - break; - case DESC_SOLID_FLUID_CONNECT: - sfconnect_count++; - ASSERT(desc->d.sf_connect.specular_fraction >= 0 - && desc->d.sf_connect.specular_fraction <= 1); - ASSERT(desc->d.sf_connect.emissivity >= 0); - ASSERT(desc->d.sf_connect.hc >= 0); - break; - case DESC_MAT_SOLID: - smed_count++; - res = create_solid(dev, - stardis->descriptions[i].d.solid.name, - stardis->descriptions[i].d.solid.lambda, - stardis->descriptions[i].d.solid.rho, - stardis->descriptions[i].d.solid.cp, - stardis->descriptions[i].d.solid.delta, - (mode & GREEN_MODE), - 0, - stardis->descriptions[i].d.solid.Tinit, - stardis->descriptions[i].d.solid.Temp, - stardis->descriptions[i].d.solid.power, - &media, - &stardis->descriptions[i].d.solid.has_power, - &desc->d.solid.solid_id); - if (res != RES_OK) goto error; - break; - case DESC_MAT_FLUID: - fmed_count++; - res = create_fluid(dev, - stardis->descriptions[i].d.fluid.name, - stardis->descriptions[i].d.fluid.rho, - stardis->descriptions[i].d.fluid.cp, - (mode & GREEN_MODE), - 0, - stardis->descriptions[i].d.fluid.Tinit, - stardis->descriptions[i].d.fluid.Temp, - &media, - &desc->d.fluid.fluid_id); - if (res != RES_OK) goto error; - - break; - default: FATAL("Invalid type.\n"); - } + struct sdis_green_function* green = NULL; + struct sdis_solve_boundary_args args = SDIS_SOLVE_BOUNDARY_ARGS_DEFAULT; + struct darray_estimators estimators; + int estimators_initialized = 0; + size_t p; + + ASSERT(stardis + && (stardis->mode & MODE_MAP_COMPUTE) + && !(stardis->mode & (MODE_BIN_GREEN | MODE_GREEN))); + + darray_estimators_init(stardis->allocator, &estimators); + estimators_initialized = 1; + + ERR(darray_estimators_resize(&estimators, + darray_estimators_size_get(&estimators) + + darray_size_t_size_get(&stardis->compute_surface.primitives))); + + FOR_EACH(p, 0, darray_size_t_size_get(&stardis->compute_surface.primitives)) { + + args.nrealisations = stardis->samples; + args.primitives + = darray_size_t_cdata_get(&stardis->compute_surface.primitives); + args.sides = darray_sides_cdata_get(&stardis->compute_surface.sides); + args.nprimitives + = darray_size_t_size_get(&stardis->compute_surface.primitives); + d2_set(args.time_range, stardis->time_range); + args.register_paths = stardis->dump_paths; + + ERR(sdis_solve_boundary(stardis->sdis_scn, &args, + darray_estimators_data_get(&estimators) + p)); } + dump_map(stardis, &estimators, stdout); - /* Create interfaces */ - htable_intface_init(&stardis->allocator, &htable_interfaces); - htable_interfaces_initialised = 1; - for (i = 0; i < sa_size(stardis->geometry.triangle); ++i) { - struct triangle *trg = stardis->geometry.triangle + i; - struct int_descs int_descs = INT_DESCS_NULL; - struct sdis_interface** p_intface; - struct sdis_interface* intface; - struct sdis_medium* front_med = NULL; - struct sdis_medium* back_med = NULL; - struct sdis_interface_side_shader* fluid_side_shader = NULL; - unsigned fd = trg->front_description; - unsigned bd = trg->back_description; - unsigned cd = trg->connection_description; - int fluid_count = 0, solid_count = 0; - int solid_fluid_connection_count = 0, connection_count = 0, boundary_count = 0; - - /* Create key */ - int_descs.front = fd; - int_descs.back = bd; - int_descs.connect = trg->connection_description; - - /* Search if interface already exists, or create it */ - p_intface = htable_intface_find(&htable_interfaces, &int_descs); - if (p_intface) { - intface = *p_intface; - } else { - /* create new interface and register */ - struct sdis_interface_shader interface_shader = SDIS_INTERFACE_SHADER_NULL; - struct intface* interface_props = NULL; - unsigned id; - int front_defined = (fd != UINT_MAX); - int back_defined = (bd != UINT_MAX); - int connect_defined = (cd != UINT_MAX); - - SDIS(data_create(dev, sizeof(struct intface), ALIGNOF(struct intface), - release_interface_data, &data)); - interface_props = sdis_data_get(data); - interface_props->temperature = NULL; - interface_props->flux = NULL; - interface_props->front_boundary_id = UINT_MAX; - interface_props->back_boundary_id = UINT_MAX; - - if (front_defined) { - switch (stardis->descriptions[fd].type) { - case DESC_MAT_SOLID: - id = stardis->descriptions[fd].d.solid.solid_id; - solid_count++; - front_med = media[id]; - break; - case DESC_MAT_FLUID: - fluid_count++; - id = stardis->descriptions[fd].d.fluid.fluid_id; - front_med = media[id]; - fluid_side_shader = &interface_shader.front; - break; - default: FATAL("Invalid type.\n"); - } - } - if (back_defined) { - switch (stardis->descriptions[bd].type) { - case DESC_MAT_SOLID: - id = stardis->descriptions[bd].d.solid.solid_id; - solid_count++; - back_med = media[id]; - break; - case DESC_MAT_FLUID: - fluid_count++; - id = stardis->descriptions[bd].d.fluid.fluid_id; - back_med = media[id]; - fluid_side_shader = &interface_shader.back; - break; - default: FATAL("Invalid type.\n"); - } - } - if (connect_defined) { - const struct description* connect = stardis->descriptions + cd; - int type_checked = 0; - struct sdis_medium* def_medium = NULL; - unsigned ext_id; - if (connect->type == DESC_SOLID_FLUID_CONNECT) { - if (solid_count != 1 || fluid_count != 1) { - ASSERT(front_defined && back_defined); - fprintf(stderr, - "Can only define a DESC_SOLID_FLUID_CONNECT between a fluid and a solid\n"); - res = RES_BAD_ARG; - goto error; - } - } else { - if (front_defined == back_defined) { - fprintf(stderr, - "Cannot define a boundary between 2 %s regions\n", - front_defined ? "defined" : "undefined"); - res = RES_BAD_ARG; - goto error; - } - def_medium = front_defined ? front_med : back_med; - if (front_defined) - interface_props->front_boundary_id = cd; - else interface_props->back_boundary_id = cd; - } - switch (connect->type) { - case DESC_BOUND_H_FOR_FLUID: - if (sdis_medium_get_type(def_medium) != SDIS_FLUID) { - fprintf(stderr, - "Can only define a DESC_BOUND_H_FOR_FLUID boundary for a fluid region\n"); - res = RES_BAD_ARG; - goto error; - } - type_checked = 1; - /* fall through */ - case DESC_BOUND_H_FOR_SOLID: - if (!type_checked - && sdis_medium_get_type(def_medium) != SDIS_SOLID) { - fprintf(stderr, - "Can only define a DESC_BOUND_H_FOR_SOLID boundary for a solid region\n"); - res = RES_BAD_ARG; - goto error; - } - ext_id = connect->d.h_boundary.mat_id; /* External material id */ - ASSERT(ext_id < sa_size(media)); - ASSERT(sdis_medium_get_type(media[ext_id]) == - (connect->type == DESC_BOUND_H_FOR_SOLID ? SDIS_FLUID : SDIS_SOLID)); - connection_count++; - boundary_count++; - if (front_defined) back_med = media[ext_id]; - else front_med = media[ext_id]; - interface_shader.convection_coef = interface_get_convection_coef; - interface_shader.convection_coef_upper_bound = connect->d.h_boundary.hc_max; - interface_props->hc = connect->d.h_boundary.hc; - ASSERT(!fluid_side_shader); /* Cause defined medium is solid */ - if (connect->d.h_boundary.has_emissivity) { - fluid_side_shader = - front_defined ? &interface_shader.back : &interface_shader.front; - fluid_side_shader->emissivity = interface_get_emissivity; - interface_props->emissivity = connect->d.h_boundary.emissivity; - interface_props->alpha = connect->d.h_boundary.specular_fraction; - } - break; - case DESC_BOUND_T_FOR_FLUID: - if (sdis_medium_get_type(def_medium) != SDIS_FLUID) { - fprintf(stderr, - "Can only define a DESC_BOUND_T_FOR_FLUID boundary for a fluid region\n"); - res = RES_BAD_ARG; - goto error; - } - type_checked = 1; - /* fall through */ - case DESC_BOUND_T_FOR_SOLID: - if (!type_checked - && sdis_medium_get_type(def_medium) != SDIS_SOLID) { - fprintf(stderr, - "Can only define a DESC_BOUND_T_FOR_SOLID boundary for a solid region\n"); - res = RES_BAD_ARG; - goto error; - } - ext_id = connect->d.t_boundary.mat_id; /* External material id */ - ASSERT(ext_id < sa_size(media)); - ASSERT(sdis_medium_get_type(media[ext_id]) == SDIS_FLUID); - connection_count++; - boundary_count++; - if (front_defined) { - back_med = media[ext_id]; - /* We set the known T inside - * TODO: should be outside to allow contact resistances */ - interface_shader.front.temperature = interface_get_temperature; - } else { - front_med = media[ext_id]; - /* We set the known T inside - * TODO: should be outside to allow contact resistances */ - interface_shader.back.temperature = interface_get_temperature; - } - ASSERT(connect->d.t_boundary.te_temperature); - interface_props->temperature - = te_duplicate(connect->d.t_boundary.te_temperature); - if (connect->d.t_boundary.has_hc) { - ASSERT(connect->type == DESC_BOUND_T_FOR_FLUID); - interface_shader.convection_coef = interface_get_convection_coef; - interface_shader.convection_coef_upper_bound = connect->d.t_boundary.hc_max; - interface_props->hc = connect->d.t_boundary.hc; - } - break; - case DESC_BOUND_F_FOR_SOLID: - if (sdis_medium_get_type(def_medium) != SDIS_SOLID) { - fprintf(stderr, - "Can only define a DESC_BOUND_F_FOR_SOLID boundary for a solid region\n"); - res = RES_BAD_ARG; - goto error; - } - connection_count++; - boundary_count++; - if (front_defined) { - back_med = media[connect->d.f_boundary.mat_id]; - interface_shader.front.flux = interface_get_flux; - } else { - front_med = media[connect->d.f_boundary.mat_id]; - interface_shader.back.flux = interface_get_flux; - } - ASSERT(connect->d.f_boundary.te_flux); - interface_props->flux = te_duplicate(connect->d.f_boundary.te_flux); - break; - case DESC_SOLID_FLUID_CONNECT: - /* Both front and back should be defined; - * if not will raise an error below */ - connection_count++; - solid_fluid_connection_count++; - ASSERT(fluid_side_shader); - if (connect->d.sf_connect.has_hc) { - interface_shader.convection_coef = interface_get_convection_coef; - interface_shader.convection_coef_upper_bound = connect->d.sf_connect.hc_max; - interface_props->hc = connect->d.sf_connect.hc; - } - if (connect->d.sf_connect.has_emissivity) { - fluid_side_shader->emissivity = interface_get_emissivity; - interface_props->emissivity = connect->d.sf_connect.emissivity; - interface_props->alpha = connect->d.sf_connect.specular_fraction; - } - break; - default: FATAL("Invalid type.\n"); - } - } - - if ((fluid_count == 2) - || (fluid_count + solid_count + connection_count < 2) - || (boundary_count ? - (fluid_count + solid_count != 1) : (fluid_count + solid_count != 2)) - || (solid_fluid_connection_count && (fluid_count != 1 || solid_count != 1))) - { - /* Incoherent triangle description */ - fprintf(stderr, "Incoherent triangle description (%u)\n", i); - print_trg_as_obj(stderr, stardis->geometry.vertex, trg->indices.data); - fprintf(stderr, "Front: "); - if (!front_defined) fprintf(stderr, "undefined\n"); - else print_description(stderr, &stardis->descriptions[fd]); - fprintf(stderr, "Back: "); - if (!back_defined) fprintf(stderr, "undefined\n"); - else print_description(stderr, &stardis->descriptions[bd]); - fprintf(stderr, "Connection: "); - if (!connect_defined) fprintf(stderr, "undefined\n"); - else print_description(stderr, &stardis->descriptions[cd]); - res = RES_BAD_ARG; - goto error; - } - - res = sdis_interface_create(dev, front_med, back_med, - &interface_shader, data, &intface); - SDIS(data_ref_put(data)); data = NULL; - if (res != RES_OK) { - fprintf(stderr, - "Cannot create interface associated to triangle %u\n", i); - goto error; - } - sa_push(interfaces, intface); - res = htable_intface_set(&htable_interfaces, &int_descs, &intface); - if (res != RES_OK) goto error; +end: + if(estimators_initialized) { + struct sdis_estimator** est = darray_estimators_data_get(&estimators); + FOR_EACH(p, 0, darray_size_t_size_get(&stardis->compute_surface.primitives)) { + SDIS(estimator_ref_put(est[p])); } - sa_push(interf_bytrg, intface); + darray_estimators_release(&estimators); } - stardis->geometry.interf_bytrg = interf_bytrg; - stardis->geometry.interfaces = interfaces; - - res = sdis_scene_create(dev, - sa_size(stardis->geometry.triangle), - geometry_get_indices, geometry_get_interface, - sa_size(stardis->geometry.vertex), - geometry_get_position, &stardis->geometry, &scn); - if (res != RES_OK) goto error; - - res = create_edge_file_if(scn, &stardis->allocator); - if (res != RES_OK) goto error; - - if (mode & GREEN_MODE) { - struct sdis_green_function* green = NULL; - size_t ok_count, failed_count; - struct w_ctx w_ctx; - ASSERT((mode & PROBE_COMPUTE) || (mode & PROBE_COMPUTE_ON_INTERFACE) - || (mode & BOUNDARY_COMPUTE) || (mode & MEDIUM_COMPUTE)); - - if (mode & PROBE_COMPUTE || mode & PROBE_COMPUTE_ON_INTERFACE) { - double uv[2] = { 0,0 }; - size_t iprim = SIZE_MAX; - /* Launch the probe simulation */ - pos[0] = stardis->probe[0]; - pos[1] = stardis->probe[1]; - pos[2] = stardis->probe[2]; - - res = select_probe_type(scn, - stardis->geometry.triangle, - stardis->geometry.vertex, - stardis->descriptions, - (mode & PROBE_COMPUTE_ON_INTERFACE), - pos, - &iprim, - uv); - if (res != RES_OK) goto error; - - if (iprim == SIZE_MAX) { - res = sdis_solve_probe_green_function(scn, - stardis->N, - pos, - stardis->scale_factor, - stardis->radiative_temp[0], - stardis->radiative_temp[1], - &green); - } else { - res = sdis_solve_probe_boundary_green_function(scn, - stardis->N, - iprim, - uv, - SDIS_FRONT, - stardis->scale_factor, - stardis->radiative_temp[0], - stardis->radiative_temp[1], - &green); - } - } - else if (mode & BOUNDARY_COMPUTE) { - ASSERT(sa_size(stardis->boundary.primitives) - == sa_size(stardis->boundary.sides)); - if (sa_size(stardis->boundary.sides) == 0) { - fprintf(stderr, "Cannot solve boundary %s (empty geometry)\n", - stardis->solve_name); - } + if(green) SDIS(green_function_ref_put(green)); + return res; +error: + goto end; +} - res = sdis_solve_boundary_green_function(scn, - stardis->N, - stardis->boundary.primitives, - stardis->boundary.sides, - sa_size(stardis->boundary.sides), - stardis->scale_factor, - stardis->radiative_temp[0], - stardis->radiative_temp[1], - &green); - } - else if (mode & MEDIUM_COMPUTE) { - size_t sz, ii; - struct sdis_medium* medium = NULL; - /* Find medium */ - sz = sa_size(stardis->descriptions); - FOR_EACH(ii, 0, sz) { - struct description* desc = stardis->descriptions + ii; - if (desc->type == DESC_MAT_SOLID) { - if (0 == strcmp(stardis->solve_name, desc->d.solid.name)) { - ASSERT(sa_size(media) > ii); - medium = media[ii]; - break; - } - } - else if (desc->type == DESC_MAT_FLUID) { - if (0 == strcmp(stardis->solve_name, desc->d.fluid.name)) { - ASSERT(sa_size(media) > ii); - medium = media[ii]; - break; - } - } - } - if (medium == NULL) { - /* Not found */ - fprintf(stderr, "Cannot solve medium %s (unknown medium)\n", - stardis->solve_name); - res = RES_BAD_ARG; - goto error; - } - res = sdis_solve_medium_green_function(scn, - stardis->N, - medium, - stardis->scale_factor, - stardis->radiative_temp[0], - stardis->radiative_temp[1], - &green); - } - if (res != RES_OK) goto error; - - /* Output the green function */ - CHK(sdis_green_function_get_invalid_paths_count(green, &failed_count) == RES_OK); - CHK(sdis_green_function_get_paths_count(green, &ok_count) == RES_OK); - - /* Output counts */ - printf("---BEGIN GREEN---\n"); - printf("# #solids #fluids #t_boundaries #h_boundaries #f_boundaries #ok #failures\n"); - printf("%zu %zu %zu %zu %zu %zu %zu\n", - smed_count, fmed_count, - tbound_count, hbound_count, fbound_count, - ok_count, failed_count); - - /* List Media */ - if (smed_count) { - printf("# Solids\n"); - printf("# ID Name lambda rho cp power\n"); - FOR_EACH(i, 0, (unsigned)sa_size(stardis->descriptions)) { - struct description* desc = stardis->descriptions + i; - struct mat_solid* sl; - if (desc->type != DESC_MAT_SOLID) continue; - sl = &desc->d.solid; - printf("%u\t%s\t%g\t%g\t%g\t%s\n", - i, sl->name, sl->lambda, sl->rho, sl->cp, (sl->has_power ? sl->power : "0")); - } - } - if (fmed_count) { - printf("# Fluids\n"); - printf("# ID Name rho cp\n"); - FOR_EACH(i, 0, sa_size(stardis->descriptions)) { - struct description* desc = stardis->descriptions + i; - struct mat_fluid* fl; - fl = &desc->d.fluid; - printf("%u\t%s\t%g\t%g\n", i, fl->name, fl->rho, fl->cp); - } - } +/******************************************************************************* + * Public Functions + ******************************************************************************/ - /* List Boundaries */ - if (tbound_count) { - printf("# T Boundaries\n"); - printf("# ID Name temperature\n"); - FOR_EACH(i, 0, sa_size(stardis->descriptions)) { - struct description* desc = stardis->descriptions + i; - struct t_boundary* bd; - if (desc->type != DESC_BOUND_T_FOR_SOLID - && desc->type != DESC_BOUND_T_FOR_FLUID) continue; - bd = &desc->d.t_boundary; - printf("%u\t%s\t%s\n", i, bd->name, bd->T); - } - } - if (hbound_count) { - printf("# H Boundaries\n"); - printf("# ID Name emissivity specular_fraction hc hc_max T_env\n"); - FOR_EACH(i, 0, sa_size(stardis->descriptions)) { - struct description* desc = stardis->descriptions + i; - struct h_boundary* bd; - if (desc->type != DESC_BOUND_H_FOR_SOLID - && desc->type != DESC_BOUND_H_FOR_FLUID) continue; - bd = &desc->d.h_boundary; - printf("%u\t%s\t%g\t%g\t%g\t%g\t%s\n", - i, bd->name, (bd->has_emissivity ? bd->emissivity : 0), - (bd->has_emissivity ? bd->specular_fraction : 0), - (bd->has_hc ? bd->hc : 0), (bd->has_hc ? bd->hc_max : 0), bd->T); +struct sdis_medium* +find_medium_by_name + (struct stardis* stardis, + const struct str* name, + unsigned* out_id) +{ + struct sdis_medium* medium = NULL; + size_t i; + + ASSERT(stardis && name); + + FOR_EACH(i, 0, darray_descriptions_size_get(&stardis->descriptions)) { + struct description* + desc = darray_descriptions_data_get(&stardis->descriptions) + i; + if(desc->type == DESC_MAT_SOLID) { + if(str_eq(name, &desc->d.solid.name)) { + unsigned id = desc->d.solid.solid_id; + ASSERT(darray_media_ptr_size_get(&stardis->media) > id); + medium = darray_media_ptr_data_get(&stardis->media)[id]; + if(out_id) *out_id = id; + break; } } - if (fbound_count) { - printf("# F Boundaries\n"); - printf("# ID Name flux\n"); - FOR_EACH(i, 0, sa_size(stardis->descriptions)) { - struct description* desc = stardis->descriptions + i; - struct f_boundary* bd; - if (desc->type != DESC_BOUND_F_FOR_SOLID) continue; - bd = &desc->d.f_boundary; - printf("%u\t%s\t%s\n", - i, bd->name, bd->flux); + else if(desc->type == DESC_MAT_FLUID) { + if(str_eq(name, &desc->d.fluid.name)) { + unsigned id = desc->d.fluid.fluid_id; + ASSERT(darray_media_ptr_size_get(&stardis->media) > id); + medium = darray_media_ptr_data_get(&stardis->media)[id]; + if(out_id) *out_id = id; + break; } } + } + return medium; +} - /* Radiative Temperatures */ - printf("# Radiative Temperatures\n"); - printf("# ID Rad_Temp Lin_Temp\n"); - printf("%u\t%g\t%g\n", (unsigned)sa_size(stardis->descriptions), - stardis->radiative_temp[0], stardis->radiative_temp[1]); - - printf("# Samples\n"); - printf("# end #power_terms #flux_terms power_term_1 ... power_term_n flux_term_1 ... flux_term_n\n"); - printf("# end = end_type end_id; end_type = T | H | X | R | F | S\n"); - printf("# power_term_i = power_type_i power_id_i factor_i\n"); - printf("# flux_term_i = flux_id_i factor_i\n"); - - w_ctx.desc = stardis->descriptions; - htable_weigth_init(NULL, &w_ctx.weigths); - - res = sdis_green_function_for_each_path(green, print_sample, &w_ctx); - htable_weigth_release(&w_ctx.weigths); - if (res != RES_OK) goto error; - - - printf("---END GREEN---\n"); +/* Process an STL file describing a compute region + * Triangles must be members of the geometry already */ +res_T +read_compute_surface + (struct stardis* stardis) +{ + res_T res = RES_OK; + struct sstl* sstl = NULL; + struct add_geom_ctx add_geom_ctx; + struct sg3d_geometry_add_callbacks callbacks = SG3D_ADD_CALLBACKS_NULL__; + const char* file; + + ASSERT(stardis); + + file = str_cget(&stardis->solve_name); + callbacks.get_indices = add_geom_ctx_indices; + callbacks.get_position = add_geom_ctx_position; + callbacks.add_triangle = add_compute_surface_triangle; + callbacks.merge_triangle = merge_compute_surface_triangle; + + ERR(sstl_create(stardis->logger, stardis->allocator, 0, &sstl)); + res = sstl_load(sstl, file); + if(res != RES_OK) { + logger_print(stardis->logger, LOG_ERROR, + "Cannot read STL file: '%s'\n", file); + goto error; + } + ERR(sstl_get_desc(sstl, &add_geom_ctx.stl_desc)); + ASSERT(add_geom_ctx.stl_desc.vertices_count <= UINT_MAX + && add_geom_ctx.stl_desc.triangles_count <= UINT_MAX); + + add_geom_ctx.custom = &stardis->compute_surface; + + res = sg3d_geometry_add( + stardis->geometry.sg3d, + (unsigned)add_geom_ctx.stl_desc.vertices_count, + (unsigned)add_geom_ctx.stl_desc.triangles_count, + &callbacks, + &add_geom_ctx); + if(res != RES_OK) { + logger_print(stardis->logger, LOG_ERROR, + "Cannot add file content: '%s'\n", file); + goto error; + } - CHK(sdis_green_function_ref_put(green) == RES_OK); - } else { - if (mode & IR_COMPUTE) { - size_t width = (size_t)stardis->camera.img[0]; - size_t height = (size_t)stardis->camera.img[1]; - - /* Setup the camera */ - SDIS(camera_create(dev, &cam)); - SDIS(camera_set_proj_ratio(cam, (double)width / (double)height)); - SDIS(camera_set_fov(cam, MDEG2RAD(stardis->camera.fov))); - SDIS(camera_look_at(cam, - stardis->camera.pos, - stardis->camera.tgt, - stardis->camera.up)); - - /* Launch the simulation */ - time[0] = time[1] = stardis->camera.u.time; - res = sdis_solve_camera(scn, - cam, - time, - stardis->scale_factor, - stardis->radiative_temp[0], - stardis->radiative_temp[1], - width, - height, - (size_t)stardis->camera.spp, - stardis->dump_paths, - &buf); - if (res != RES_OK) goto error; - - /* Write the image */ - dump_image(buf); - } - else if (mode & PROBE_COMPUTE || mode & PROBE_COMPUTE_ON_INTERFACE) { - double uv[2] = { 0,0 }; - size_t iprim = SIZE_MAX; - /* Launch the probe simulation */ - pos[0] = stardis->probe[0]; - pos[1] = stardis->probe[1]; - pos[2] = stardis->probe[2]; - time[0] = time[1] = stardis->probe[3]; - - res = select_probe_type(scn, - stardis->geometry.triangle, - stardis->geometry.vertex, - stardis->descriptions, - (mode & PROBE_COMPUTE_ON_INTERFACE), - pos, - &iprim, - uv); - if (res != RES_OK) goto error; - - if (iprim == SIZE_MAX) { - res = sdis_solve_probe(scn, - stardis->N, - pos, - time, - stardis->scale_factor, - stardis->radiative_temp[0], - stardis->radiative_temp[1], - stardis->dump_paths, - &estimator); - } else { - res = sdis_solve_probe_boundary(scn, - stardis->N, - iprim, - uv, - time, - SDIS_FRONT, - stardis->scale_factor, - stardis->radiative_temp[0], - stardis->radiative_temp[1], - stardis->dump_paths, - &estimator); - } - } - else if (mode & MEDIUM_COMPUTE) { - size_t sz, ii; - struct sdis_medium* medium = NULL; - /* Find medium */ - sz = sa_size(stardis->descriptions); - FOR_EACH(ii, 0, sz) { - struct description* desc = stardis->descriptions + ii; - if (desc->type == DESC_MAT_SOLID) { - if (0 == strcmp(stardis->solve_name, desc->d.solid.name)) { - ASSERT(sa_size(media) > ii); - medium = media[ii]; - break; - } - } - else if (desc->type == DESC_MAT_FLUID) { - if (0 == strcmp(stardis->solve_name, desc->d.fluid.name)) { - ASSERT(sa_size(media) > ii); - medium = media[ii]; - break; - } - } - } - if (medium == NULL) { - /* Not found */ - fprintf(stderr, "Cannot solve medium %s (unknown medium)\n", - stardis->solve_name); - res = RES_BAD_ARG; - goto error; - } - time[0] = time[1] = stardis->probe[3]; - res = sdis_solve_medium(scn, - stardis->N, - medium, - time, - stardis->scale_factor, - stardis->radiative_temp[0], - stardis->radiative_temp[1], - stardis->dump_paths, - &estimator); - } - else if (mode & BOUNDARY_COMPUTE) { - ASSERT(sa_size(stardis->boundary.primitives) - == sa_size(stardis->boundary.sides)); - if (sa_size(stardis->boundary.sides) == 0) { - fprintf(stderr, "Cannot solve boundary %s (empty geometry)\n", - stardis->solve_name); - } +end: + if(sstl) SSTL(ref_put(sstl)); + return res; +error: + goto end; +} - time[0] = time[1] = stardis->probe[3]; - res = sdis_solve_boundary(scn, - stardis->N, - stardis->boundary.primitives, - stardis->boundary.sides, - sa_size(stardis->boundary.sides), - time, - stardis->scale_factor, - stardis->radiative_temp[0], - stardis->radiative_temp[1], - stardis->dump_paths, - &estimator); - } else { - CHK(0); - } - if (res != RES_OK) goto error; - if (mode & PROBE_COMPUTE - || mode & PROBE_COMPUTE_ON_INTERFACE - || mode & MEDIUM_COMPUTE - || mode & BOUNDARY_COMPUTE) { - /* Fetch the estimation data */ - SDIS(estimator_get_temperature(estimator, &temperature)); - SDIS(estimator_get_failure_count(estimator, &nfailures)); - - /* Print the results */ - switch (mode & COMPUTE_MODES) { - case PROBE_COMPUTE: - case PROBE_COMPUTE_ON_INTERFACE: - printf("Temperature at t=[%g, %g, %g, %g] = %g +/- %g\n", - pos[0], pos[1], pos[2], time[0], - temperature.E, /* Expected value */ - temperature.SE); /* Standard error */ - break; - case MEDIUM_COMPUTE: - printf("Temperature in medium %s at t=%g = %g +/- %g\n", - stardis->solve_name, time[0], - temperature.E, /* Expected value */ - temperature.SE); /* Standard error */ - break; - case BOUNDARY_COMPUTE: - printf("Temperature at boundary %s at t=%g = %g +/- %g\n", - stardis->solve_name, time[0], - temperature.E, /* Expected value */ - temperature.SE); /* Standard error */ - break; - default: FATAL("Invalid mode."); - } - printf("#failures: %lu/%lu\n", - (unsigned long)nfailures, - (unsigned long)stardis->N); +res_T +stardis_compute + (struct stardis* stardis) +{ + res_T res = RES_OK; - /* Dump paths according to user settings */ - sdis_estimator_for_each_path(estimator, dump_path, stdout); - } - } + ASSERT(stardis && (stardis->mode & COMPUTE_MODES)); + + /* Compute */ + if(stardis->mode & MODE_PROBE_COMPUTE) + ERR(compute_probe(stardis)); + else if(stardis->mode & MODE_PROBE_COMPUTE_ON_INTERFACE) + ERR(compute_probe_on_interface(stardis)); + else if(stardis->mode & MODE_IR_COMPUTE) + ERR(compute_camera(stardis)); + else if(stardis->mode & MODE_MEDIUM_COMPUTE) + ERR(compute_medium(stardis)); + else if(stardis->mode & MODE_BOUNDARY_COMPUTE) + ERR(compute_boundary(stardis)); + else if(stardis->mode & MODE_FLUX_BOUNDARY_COMPUTE) + ERR(compute_flux_boundary(stardis)); + else if(stardis->mode & MODE_MAP_COMPUTE) + ERR(compute_map(stardis)); + else FATAL("Unknown mode.\n"); end: - if (data) SDIS(data_ref_put(data)); - if (interfaces) { - for (i = 0; i < sa_size(interfaces); ++i) - SDIS(interface_ref_put(interfaces[i])); - sa_release(interfaces); - stardis->geometry.interfaces = NULL; - } - sa_release(interf_bytrg); - stardis->geometry.interf_bytrg = NULL; - if (htable_interfaces_initialised) htable_intface_release(&htable_interfaces); - for (i = 0; i < sa_size(media); ++i) - SDIS(medium_ref_put(media[i])); - sa_release(media); - - if (cam) SDIS(camera_ref_put(cam)); - if (buf) SDIS(estimator_buffer_ref_put(buf)); - if (estimator) SDIS(estimator_ref_put(estimator)); - if (scn) SDIS(scene_ref_put(scn)); - if (dev) SDIS(device_ref_put(dev)); - return res; error: + logger_print(stardis->logger, LOG_ERROR, + "%s: computation failed!\n", FUNC_NAME); goto end; } diff --git a/src/stardis-compute.h b/src/stardis-compute.h @@ -0,0 +1,43 @@ +/* Copyright (C) 2018-2020 |Meso|Star> (contact@meso-star.com) + * + * This program 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. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef SDIS_COMPUTE_H +#define SDIS_COMPUTE_H + +#include <rsys/rsys.h> +#include <rsys/dynamic_array.h> + +struct stardis; +struct str; + +#define DARRAY_NAME estimators +#define DARRAY_DATA struct sdis_estimator* +#include <rsys/dynamic_array.h> + +struct sdis_medium* +find_medium_by_name + (struct stardis* stardis, + const struct str* name, + unsigned* id); /* Can be NULL */ + +extern LOCAL_SYM res_T +stardis_compute + (struct stardis* stardis); + +extern LOCAL_SYM res_T +read_compute_surface + (struct stardis* stardis); + +#endif +\ No newline at end of file diff --git a/src/stardis-default.h.in b/src/stardis-default.h.in @@ -0,0 +1,37 @@ +/* Copyright (C) 2018-2020 |Meso|Star> + * + * This program 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. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef STARDIS_DEFAULT_H +#define STARDIS_DEFAULT_H +#define STARDIS_RENDERING_FMT(Fmt) STARDIS_RENDERING_OUTPUT_FILE_FMT_ ## Fmt + +#define STARDIS_DEFAULT_AMBIENT_TEMP @STARDIS_ARGS_DEFAULT_AMBIENT_TEMP@ +#define STARDIS_DEFAULT_COMPUTE_TIME @STARDIS_ARGS_DEFAULT_COMPUTE_TIME@ +#define STARDIS_DEFAULT_RENDERING_FOV @STARDIS_ARGS_DEFAULT_RENDERING_FOV@ +#define STARDIS_DEFAULT_RENDERING_IMG_HEIGHT @STARDIS_ARGS_DEFAULT_RENDERING_IMG_HEIGHT@ +#define STARDIS_DEFAULT_RENDERING_IMG_WIDTH @STARDIS_ARGS_DEFAULT_RENDERING_IMG_WIDTH@ +#define STARDIS_DEFAULT_RENDERING_OUTPUT_FILE_FMT STARDIS_RENDERING_FMT( @STARDIS_ARGS_DEFAULT_RENDERING_OUTPUT_FILE_FMT@ ) +#define STARDIS_DEFAULT_RENDERING_POS @STARDIS_ARGS_DEFAULT_RENDERING_POS@ +#define STARDIS_DEFAULT_RENDERING_SPP @STARDIS_ARGS_DEFAULT_RENDERING_SPP@ +#define STARDIS_DEFAULT_RENDERING_TGT @STARDIS_ARGS_DEFAULT_RENDERING_TGT@ +#define STARDIS_DEFAULT_RENDERING_TIME @STARDIS_ARGS_DEFAULT_RENDERING_TIME@ +#define STARDIS_DEFAULT_RENDERING_UP @STARDIS_ARGS_DEFAULT_RENDERING_UP@ +#define STARDIS_DEFAULT_REFERENCE_TEMP @STARDIS_ARGS_DEFAULT_REFERENCE_TEMP@ +#define STARDIS_DEFAULT_SAMPLES_COUNT @STARDIS_ARGS_DEFAULT_SAMPLES_COUNT@ +#define STARDIS_DEFAULT_SCALE_FACTOR @STARDIS_ARGS_DEFAULT_SCALE_FACTOR@ +#define STARDIS_DEFAULT_VERBOSE_LEVEL @STARDIS_ARGS_DEFAULT_VERBOSE_LEVEL@ + +#endif /* STARDIS_DEFAULT_H */ + diff --git a/src/stardis-fluid.c b/src/stardis-fluid.c @@ -0,0 +1,164 @@ +/* Copyright (C) 2018-2020 |Meso|Star> (contact@meso-star.com) + * + * This program 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. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "stardis-fluid.h" +#include "stardis-compute.h" +#include "stardis-app.h" + +#include <sdis.h> + +#include <limits.h> + +/******************************************************************************* + * Local Functions + ******************************************************************************/ + +static double +fluid_get_calorific_capacity + (const struct sdis_rwalk_vertex* vtx, + struct sdis_data* data) +{ + const struct fluid* fluid_props = sdis_data_cget(data); + (void)vtx; + return fluid_props->cp; +} + +static double +fluid_get_volumic_mass + (const struct sdis_rwalk_vertex* vtx, + struct sdis_data* data) +{ + const struct fluid* fluid_props = sdis_data_cget(data); + (void)vtx; + return fluid_props->rho; +} + +static double +fluid_get_temperature + (const struct sdis_rwalk_vertex* vtx, + struct sdis_data* data) +{ + const struct fluid* fluid_props = sdis_data_cget(data); + if(fluid_props->imposed_temperature >= 0) + /* If there is an imposed temp, it is imposed regardless of time */ + return fluid_props->imposed_temperature; + if(vtx->time <= fluid_props->t0) { + /* If time is <= t0: use tinit */ + if(fluid_props->tinit < 0) { + if(str_is_empty(&fluid_props->name)) { + FATAL("fluid_get_temperature: getting undefined Tinit\n"); + } else { + VFATAL("fluid_get_temperature: getting undefined Tinit (fluid '%s')\n", + ARG1(str_cget(&fluid_props->name))); + } + } + return fluid_props->tinit; + } + return -1; /* Unknown temperature */ +} + +/******************************************************************************* + * Public Functions + ******************************************************************************/ + +res_T +create_solver_fluid + (struct stardis* stardis, + const struct fluid* fluid_props) +{ + res_T res = RES_OK; + struct sdis_fluid_shader fluid_shader = SDIS_FLUID_SHADER_NULL; + struct sdis_data* data = NULL; + struct fluid* props; + + ASSERT(stardis && fluid_props); + fluid_shader.calorific_capacity = fluid_get_calorific_capacity; + fluid_shader.volumic_mass = fluid_get_volumic_mass; + fluid_shader.temperature = fluid_get_temperature; + ERR(sdis_data_create(stardis->dev, sizeof(struct fluid), ALIGNOF(struct fluid), + NULL, &data)); + + props = sdis_data_get(data); /* Fetch the allocated memory space */ + init_fluid(stardis->allocator, props); + cp_fluid(props, fluid_props); + if(fluid_props->fluid_id >= darray_media_ptr_size_get(&stardis->media)) { + ERR(darray_media_ptr_resize(&stardis->media, fluid_props->fluid_id + 1)); + } + ASSERT(darray_media_ptr_data_get(&stardis->media)[fluid_props->fluid_id] == NULL); + ERR(sdis_fluid_create(stardis->dev, &fluid_shader, data, + darray_media_ptr_data_get(&stardis->media) + fluid_props->fluid_id)); + +end: + if(data) SDIS(data_ref_put(data)); + return res; +error: + goto end; +} + +void +init_fluid(struct mem_allocator* allocator, struct fluid* dst) +{ + str_init(allocator, &dst->name); + dst->rho = 1; + dst->cp = 1; + dst->tinit = -1; + dst->imposed_temperature = -1; + dst->t0 = 0; + dst->is_outside = 0; + dst->is_green = 0; + dst->desc_id = UINT_MAX; + dst->fluid_id = UINT_MAX; +} + +void +release_fluid(struct fluid* fluid) +{ + str_release(&fluid->name); +} + +res_T +str_print_fluid(struct str* str, const struct fluid* f) +{ + res_T res = RES_OK; + ASSERT(str && f); + STR_APPEND_PRINTF(str, "Fluid '%s': rho=%g cp=%g", + ARG3( str_cget(&f->name), f->rho, f->cp ) ); + if(f->tinit >= 0) { + STR_APPEND_PRINTF(str, " Tinit=%g", ARG1( f->tinit ) ); + } + if(f->imposed_temperature >= 0) { + STR_APPEND_PRINTF(str, " Temp=%g", ARG1( f->imposed_temperature ) ); + } + STR_APPEND_PRINTF(str, " (it is medium %u)", ARG1( f->fluid_id ) ); +end: + return res; +error: + goto end; +} + +res_T +cp_fluid(struct fluid* dst, const struct fluid* src) +{ + dst->rho = src->rho; + dst->cp = src->cp; + dst->tinit = src->tinit; + dst->imposed_temperature = src->imposed_temperature; + dst->t0 = src->t0; + dst->is_outside = src->is_outside; + dst->is_green = src->is_green; + dst->desc_id = src->desc_id; + dst->fluid_id = src->fluid_id; + return str_copy(&dst->name, &src->name); +} diff --git a/src/stardis-fluid.h b/src/stardis-fluid.h @@ -0,0 +1,59 @@ +/* Copyright (C) 2018-2020 |Meso|Star> (contact@meso-star.com) + * + * This program 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. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef SDIS_FLUID_H +#define SDIS_FLUID_H + +#include <sdis.h> + +#include <rsys/rsys.h> +#include <rsys/str.h> + +struct stardis; + +/******************************************************************************* + * Fluid data + ******************************************************************************/ +struct fluid { + struct str name; + double rho; /* Volumic mass */ + double cp; /* Calorific capacity */ + double tinit; + double imposed_temperature; + double t0; /* End time of tinit */ + int is_outside; /* the fluid is used for a boundary */ + int is_green; /* green computation (nothing to do with fluid itself) */ + unsigned desc_id; /* id of the boundary; meaningful if is_outside */ + unsigned fluid_id; +}; + +res_T +create_solver_fluid + (struct stardis* stardis, + const struct fluid* fluid_props); + +LOCAL_SYM void +init_fluid(struct mem_allocator* allocator, struct fluid* dst); + +LOCAL_SYM void +release_fluid(struct fluid* fluid); + +LOCAL_SYM res_T +str_print_fluid(struct str* str, const struct fluid* s); + +LOCAL_SYM res_T +cp_fluid(struct fluid* dst, const struct fluid* src); + +#endif diff --git a/src/stardis-intface.c b/src/stardis-intface.c @@ -0,0 +1,364 @@ +/* Copyright (C) 2018-2020 |Meso|Star> (contact@meso-star.com) + * + * This program 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. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "stardis-intface.h" +#include "stardis-app.h" +#include "stardis-compute.h" +#include "stardis-output.h" +#include "stardis-solid.h" + +#include <sdis.h> + +/******************************************************************************* + * Local Functions + ******************************************************************************/ + +static double +interface_get_convection_coef + (const struct sdis_interface_fragment* frag, + struct sdis_data* data) +{ + const struct intface* interface_props = sdis_data_cget(data); + (void)frag; + return interface_props->hc; +} + +static double +interface_get_temperature + (const struct sdis_interface_fragment* frag, + struct sdis_data* data) +{ + const struct intface* interface_props = sdis_data_cget(data); + (void)frag; + return interface_props->imposed_temperature; +} + +static double +interface_get_flux + (const struct sdis_interface_fragment* frag, + struct sdis_data* data) +{ + const struct intface* interface_props = sdis_data_cget(data); + (void)frag; + return interface_props->imposed_flux; +} + +static double +interface_get_emissivity + (const struct sdis_interface_fragment* frag, + struct sdis_data* data) +{ + const struct intface* interface_props = sdis_data_cget(data); + (void)frag; + return interface_props->emissivity; +} + +static double +interface_get_alpha + (const struct sdis_interface_fragment* frag, + struct sdis_data* data) +{ + const struct intface* interface_props = sdis_data_cget(data); + (void)frag; + return interface_props->alpha; +} + +/******************************************************************************* + * Public Functions + ******************************************************************************/ + +res_T +create_intface + (struct stardis* stardis, + unsigned itri, + struct htable_intface* htable_interfaces) +{ + struct int_descs int_descs = INT_DESCS_NULL; + struct sdis_interface** p_intface; + struct sdis_interface* intface; + struct sdis_medium* front_med = NULL; + struct sdis_medium* back_med = NULL; + struct sdis_interface_side_shader* fluid_side_shader = NULL; + struct sdis_data* data = NULL; + unsigned fd, bd, cd, descr[SG3D_PROP_TYPES_COUNT__]; + int fluid_count = 0, solid_count = 0; + int solid_fluid_connection_count = 0, connection_count = 0, boundary_count = 0; + const struct description* descriptions; + struct sdis_medium** media; + res_T res = RES_OK; + + ASSERT(stardis && htable_interfaces); + + /* Get the description IDs for this triangle */ + ERR(sg3d_geometry_get_unique_triangle_properties(stardis->geometry.sg3d, + itri, descr)); + + descriptions = darray_descriptions_cdata_get(&stardis->descriptions); + media = darray_media_ptr_data_get(&stardis->media); + + /* Create key */ + int_descs.front = fd = descr[SG3D_FRONT]; + int_descs.back = bd = descr[SG3D_BACK]; + int_descs.connect = cd = descr[SG3D_INTFACE]; + + /* Search if interface already exists, or create it */ + p_intface = htable_intface_find(htable_interfaces, &int_descs); + if(p_intface) { + intface = *p_intface; + } else { + /* create new interface and register */ + struct sdis_interface_shader interface_shader = SDIS_INTERFACE_SHADER_NULL; + struct intface* interface_props = NULL; + unsigned id; + int front_defined = (fd != SG3D_UNSPECIFIED_PROPERTY); + int back_defined = (bd != SG3D_UNSPECIFIED_PROPERTY); + int connect_defined = (cd != SG3D_UNSPECIFIED_PROPERTY); + + ERR(sdis_data_create(stardis->dev, sizeof(struct intface), + ALIGNOF(struct intface), NULL, &data)); + interface_props = sdis_data_get(data); + interface_props->imposed_temperature = -1; + interface_props->imposed_flux = SDIS_FLUX_NONE; + interface_props->front_medium_id = UINT_MAX; + interface_props->back_medium_id = UINT_MAX; + interface_props->desc_id = UINT_MAX; + + if(front_defined) { + switch (descriptions[fd].type) { + case DESC_MAT_SOLID: + id = descriptions[fd].d.solid.solid_id; + solid_count++; + interface_props->front_medium_id = id; + front_med = media[id]; + break; + case DESC_MAT_FLUID: + fluid_count++; + id = descriptions[fd].d.fluid.fluid_id; + interface_props->front_medium_id = id; + front_med = media[id]; + fluid_side_shader = &interface_shader.front; + break; + default: + FATAL("error:" STR(__FILE__) ":" STR(__LINE__)": Invalid type.\n"); + } + } + if(back_defined) { + switch (descriptions[bd].type) { + case DESC_MAT_SOLID: + id = descriptions[bd].d.solid.solid_id; + solid_count++; + interface_props->back_medium_id = id; + back_med = media[id]; + break; + case DESC_MAT_FLUID: + fluid_count++; + id = descriptions[bd].d.fluid.fluid_id; + interface_props->back_medium_id = id; + back_med = media[id]; + /* Can overwrite fluid_side_shader. However it would imply two + * fluids and this case lead to an error */ + fluid_side_shader = &interface_shader.back; + break; + default: + FATAL("error:" STR(__FILE__) ":" STR(__LINE__)": Invalid type.\n"); + } + } + if(fluid_count == 2) { + res = RES_BAD_ARG; + goto error; + } + if(connect_defined) { + const struct description* connect = descriptions + cd; + int type_checked = 0; + struct sdis_medium* def_medium = NULL; + unsigned ext_id; + if(connect->type != DESC_SOLID_FLUID_CONNECT + && front_defined == back_defined) + { + /* 1 and only 1 defined */ + res = RES_BAD_ARG; + goto error; + } + if(front_defined) { + def_medium = front_med; + fluid_side_shader = (descriptions[fd].type == DESC_MAT_FLUID) + ? &interface_shader.front : &interface_shader.back; + } else { + ASSERT(back_defined); + def_medium = back_med; + fluid_side_shader = (descriptions[bd].type == DESC_MAT_FLUID) + ? &interface_shader.back : &interface_shader.front; + } + interface_props->desc_id = cd; + switch(connect->type) { + case DESC_BOUND_H_FOR_FLUID: + if(sdis_medium_get_type(def_medium) != SDIS_FLUID) { + res = RES_BAD_ARG; + goto error; + } + type_checked = 1; + ASSERT(connect->d.h_boundary.imposed_temperature >= 0); + interface_props->imposed_temperature + = connect->d.h_boundary.imposed_temperature; + ASSERT(fluid_side_shader); + fluid_side_shader->temperature = interface_get_temperature; + /* fall through */ + case DESC_BOUND_H_FOR_SOLID: + if(!type_checked + && sdis_medium_get_type(def_medium) != SDIS_SOLID) + { + res = RES_BAD_ARG; goto error; + } + ext_id = connect->d.h_boundary.mat_id; /* External material id */ + ASSERT(ext_id < darray_media_ptr_size_get(&stardis->media)); + ASSERT(sdis_medium_get_type(media[ext_id]) == + (connect->type == DESC_BOUND_H_FOR_SOLID ? SDIS_FLUID : SDIS_SOLID)); + connection_count++; + boundary_count++; + if(front_defined) back_med = media[ext_id]; + else front_med = media[ext_id]; + interface_shader.convection_coef_upper_bound = connect->d.h_boundary.hc; + interface_props->hc = connect->d.h_boundary.hc; + interface_props->emissivity = connect->d.h_boundary.emissivity; + interface_props->alpha = connect->d.h_boundary.specular_fraction; + if(connect->d.h_boundary.hc > 0) { + interface_shader.convection_coef = interface_get_convection_coef; + } + if(connect->d.h_boundary.emissivity > 0) { + ASSERT(fluid_side_shader); + fluid_side_shader->emissivity = interface_get_emissivity; + fluid_side_shader->specular_fraction = interface_get_alpha; + } + break; + case DESC_BOUND_T_FOR_FLUID: + if(sdis_medium_get_type(def_medium) != SDIS_FLUID) { + res = RES_BAD_ARG; + goto error; + } + type_checked = 1; + interface_shader.convection_coef_upper_bound = connect->d.t_boundary.hc; + interface_props->hc = connect->d.t_boundary.hc; + if(connect->d.t_boundary.hc > 0) { + ASSERT(connect->type == DESC_BOUND_T_FOR_FLUID); + interface_shader.convection_coef = interface_get_convection_coef; + } + interface_props->emissivity = connect->d.t_boundary.emissivity; + interface_props->alpha = connect->d.t_boundary.specular_fraction; + if(connect->d.t_boundary.emissivity > 0) { + ASSERT(fluid_side_shader); + fluid_side_shader->emissivity = interface_get_emissivity; + fluid_side_shader->specular_fraction = interface_get_alpha; + } + /* fall through */ + case DESC_BOUND_T_FOR_SOLID: + if(!type_checked + && sdis_medium_get_type(def_medium) != SDIS_SOLID) + { + res = RES_BAD_ARG; + goto error; + } + ext_id = connect->d.t_boundary.mat_id; /* External material id */ + ASSERT(ext_id < darray_media_ptr_size_get(&stardis->media)); + ASSERT(sdis_medium_get_type(media[ext_id]) + == (connect->type == DESC_BOUND_T_FOR_FLUID ? SDIS_SOLID: SDIS_FLUID)); + connection_count++; + boundary_count++; + if(front_defined) { + ASSERT(!back_med); + back_med = media[ext_id]; + } else { + ASSERT(!front_med); + front_med = media[ext_id]; + } + /* The imposed T is for the 2 sides (until there is contact resistnces) */ + interface_shader.front.temperature = interface_get_temperature; + interface_shader.back.temperature = interface_get_temperature; + if(connect->type == DESC_BOUND_T_FOR_SOLID) { + /* Set emissivity to 1 to allow radiative paths comming from + * a possible external fluid to 'see' the imposed T */ + ASSERT(fluid_side_shader); + fluid_side_shader->emissivity = interface_get_emissivity; + interface_props->emissivity = 1; + } + ASSERT(connect->d.t_boundary.imposed_temperature >= 0); + interface_props->imposed_temperature + = connect->d.t_boundary.imposed_temperature; + break; + case DESC_BOUND_F_FOR_SOLID: + if(sdis_medium_get_type(def_medium) != SDIS_SOLID) { + res = RES_BAD_ARG; + goto error; + } + connection_count++; + boundary_count++; + if(front_defined) { + back_med = media[connect->d.f_boundary.mat_id]; + interface_shader.front.flux = interface_get_flux; + } else { + front_med = media[connect->d.f_boundary.mat_id]; + interface_shader.back.flux = interface_get_flux; + } + ASSERT(connect->d.f_boundary.imposed_flux != SDIS_FLUX_NONE); + interface_props->imposed_flux = connect->d.f_boundary.imposed_flux; + break; + case DESC_SOLID_FLUID_CONNECT: + /* Both front and back should be defined */ + if(solid_count != 1 || fluid_count != 1) { + res = RES_BAD_ARG; + goto error; + } + ASSERT(front_defined && back_defined); + connection_count++; + solid_fluid_connection_count++; + interface_shader.convection_coef_upper_bound = connect->d.sf_connect.hc; + interface_props->hc = connect->d.sf_connect.hc; + interface_props->emissivity = connect->d.sf_connect.emissivity; + interface_props->alpha = connect->d.sf_connect.specular_fraction; + if(connect->d.sf_connect.hc > 0) { + interface_shader.convection_coef = interface_get_convection_coef; + } + if(connect->d.sf_connect.emissivity > 0) { + ASSERT(fluid_side_shader); + fluid_side_shader->emissivity = interface_get_emissivity; + fluid_side_shader->specular_fraction = interface_get_alpha; + } + break; + default: + FATAL("error:" STR(__FILE__) ":" STR(__LINE__)": Invalid type.\n"); + } + } + + if((fluid_count + solid_count + connection_count < 2) + || (boundary_count && (fluid_count + solid_count != 1))) + { + res = RES_BAD_ARG; + goto error; + } + + ERR(sdis_interface_create(stardis->dev, front_med, back_med, + &interface_shader, data, &intface)); + SDIS(data_ref_put(data)); data = NULL; + ERR(darray_interface_ptrs_push_back(&stardis->geometry.interfaces, &intface)); + ERR(htable_intface_set(htable_interfaces, &int_descs, &intface)); + } + ERR(darray_interface_ptrs_push_back(&stardis->geometry.interf_bytrg, &intface)); + +end: + if(data) SDIS(data_ref_put(data)); + return res; +error: + goto end; +} + diff --git a/src/stardis-intface.h b/src/stardis-intface.h @@ -0,0 +1,67 @@ +/* Copyright (C) 2018-2020 |Meso|Star> (contact@meso-star.com) + * + * This program 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. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef SDIS_INTFACE_H +#define SDIS_INTFACE_H + +#include <rsys/hash_table.h> + +#include <limits.h> + +struct stardis; +struct dummies; + +/******************************************************************************* + * Interface data + ******************************************************************************/ +struct intface { + double hc; + double emissivity; + double alpha; + /* Imposed compute temperature & flux */ + double imposed_temperature; + double imposed_flux; + /* IDs */ + unsigned front_medium_id, back_medium_id; + unsigned desc_id; /* The description this interfaces comes from */ +}; + +/* Declare the hash table that map an interface to its descriptor */ +struct int_descs { + unsigned front, back, connect; +}; +#define INT_DESCS_NULL__ { UINT_MAX, UINT_MAX, UINT_MAX } +static const struct int_descs INT_DESCS_NULL = INT_DESCS_NULL__; + +static INLINE char +eq_desc(const struct int_descs* a, const struct int_descs* b) +{ + return (char)(a->front == b->front && a->back == b->back + && a->connect == b->connect); +} + +#define HTABLE_NAME intface +#define HTABLE_DATA struct sdis_interface* +#define HTABLE_KEY struct int_descs +#define HTABLE_KEY_FUNCTOR_EQ eq_desc +#include <rsys/hash_table.h> + +extern res_T +create_intface + (struct stardis* stardis, + unsigned tr_idx, + struct htable_intface* htable_interfaces); + +#endif diff --git a/src/stardis-main.c b/src/stardis-main.c @@ -0,0 +1,125 @@ +/* Copyright (C) 2018-2020 |Meso|Star> (contact@meso-star.com) + * + * This program 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. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "stardis-app.h" +#include "stardis-parsing.h" +#include "stardis-output.h" +#include "stardis-compute.h" + +#include <rsys/rsys.h> +#include <rsys/mem_allocator.h> +#include <rsys/logger.h> + +#include <stdlib.h> +#include <stdio.h> + +int +main + (int argc, + char** argv) +{ + struct args* args = NULL; + struct stardis stardis; + int logger_initialized = 0, allocator_initialized = 0, + args_initialized = 0, stardis_initialized = 0; + int err = EXIT_SUCCESS; + struct mem_allocator allocator; + struct logger logger; + int mode = 0; + res_T res = RES_OK; + + ERR(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); + allocator_initialized = 1; + + ERR(logger_init(&allocator, &logger)); + logger_initialized = 1; + /* Active loggin for args pasing */ + logger_set_stream(&logger, LOG_ERROR, log_err_fn, NULL); + logger_set_stream(&logger, LOG_WARNING, log_warn_fn, NULL); + logger_set_stream(&logger, LOG_OUTPUT, log_prt_fn, NULL); + + ERR(init_args(&logger, &allocator, &args)); + args_initialized = 1; + ERR(parse_args(argc, argv, args)); + mode = args->mode; + + if(mode & MODE_DUMP_HELP) { + short_help(stdout, argv[0]); + goto exit; + } + else if(mode & MODE_DUMP_VERSION) { + print_version(stdout); + goto exit; + } + + /* Deactivate some loggin according to the -V arg */ + if(args->verbose < 1) + logger_set_stream(&logger, LOG_ERROR, NULL, NULL); + if(args->verbose < 2) + logger_set_stream(&logger, LOG_WARNING, NULL, NULL); + if(args->verbose < 3) + logger_set_stream(&logger, LOG_OUTPUT, NULL, NULL); + + ERR(stardis_init(args, &logger, &allocator, &stardis)); + stardis_initialized = 1; + release_args(args); + args_initialized = 0; + + if(mode & MODE_DUMP_VTK) { + /* Dump all the app-independent information */ + ERR(sg3d_geometry_dump_as_vtk(stardis.geometry.sg3d, stdout)); + /* Dump boundaries + * Should we dump connections too? */ + ERR(dump_boundaries_at_the_end_of_vtk(&stardis, stdout)); + /* Dump the compute region if any */ + if(mode & REGION_COMPUTE_MODES) { + ERR(dump_compute_region_at_the_end_of_vtk(&stardis, stdout)); + } + ERR(dump_enclosure_related_stuff_at_the_end_of_vtk(&stardis, stdout)); + + /* If dump, exit after dump done */ + goto exit; + } + else if(mode & MODE_DUMP_C_CHUNKS) { + ERR(dump_model_as_c_chunks(&stardis, stdout)); + /* If dump, exit after dump done */ + goto exit; + } + + ASSERT(mode & COMPUTE_MODES); + ERR(stardis_compute(&stardis)); + +exit: + if(args_initialized) release_args(args); + if(stardis_initialized) stardis_release(&stardis); + if(logger_initialized) logger_release(&logger); + if(allocator_initialized) { + if(MEM_ALLOCATED_SIZE(&allocator) != 0) { + char dump[4096] = { '\0' }; + MEM_DUMP(&allocator, dump, sizeof(dump)); + fprintf(stderr, "%s\n", dump); + fprintf(stderr, "\nMemory leaks: %lu Bytes\n", + (unsigned long)MEM_ALLOCATED_SIZE(&allocator)); + } + mem_shutdown_proxy_allocator(&allocator); + } + return err; +error: + if(mode & COMPUTE_MODES) + logger_print(&logger, LOG_ERROR, + "No computation possible.\n"); + err = EXIT_FAILURE; + goto exit; +} diff --git a/src/stardis-output.c b/src/stardis-output.c @@ -0,0 +1,1727 @@ +/* Copyright (C) 2018-2020 |Meso|Star> (contact@meso-star.com) + * + * This program 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. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +#define _POSIX_C_SOURCE 200112L /* snprintf */ +#include "stardis-output.h" +#include "stardis-compute.h" +#include "stardis-fluid.h" +#include "stardis-solid.h" +#include "stardis-intface.h" +#include "stardis-app.h" + +#include <sdis.h> + +#include<star/senc3d.h> +#include<star/sg3d.h> + +#include <rsys/math.h> +#include <rsys/mem_allocator.h> +#include <rsys/dynamic_array_uint.h> +#include <rsys/hash_table.h> +#include <rsys/logger.h> +#include <rsys/str.h> + +#include <limits.h> +#include <string.h> +#include <stdio.h> + +#define HTABLE_NAME weigth +#define HTABLE_DATA double +#define HTABLE_KEY unsigned +#include <rsys/hash_table.h> + +struct w_ctx { + struct mem_allocator* alloc; + const struct darray_descriptions* desc; + struct htable_weigth pw; + struct htable_weigth flux; + FILE* stream; +}; + +struct e_ctx { + const struct darray_descriptions* desc; + FILE* stream; +}; + +/******************************************************************************* + * Local Type; for documentation purpose + * These values are used in dumps + ******************************************************************************/ +enum enclosure_errors_t { + NO_ENCLOSURE_ERROR = BIT(0), + ENCLOSURE_WITH_N_MEDIA = BIT(1), + ENCLOSURE_WITH_UNDEF_MEDIUM = BIT(2) +}; + +/******************************************************************************* + * Local Functions + ******************************************************************************/ + +static res_T +merge_flux_terms + (struct sdis_interface* interf, + const enum sdis_side side, + const double flux_term, + void* ctx) +{ + res_T res = RES_OK; + struct sdis_data* data = NULL; + struct intface* d__; + unsigned desc_id; + struct w_ctx* w_ctx = ctx; + const struct description* descs; + + ASSERT(interf && w_ctx); + (void)side; + + data = sdis_interface_get_data(interf); + d__ = sdis_data_get(data); + desc_id = d__->desc_id; + descs = darray_descriptions_cdata_get(w_ctx->desc); + + switch (descs[desc_id].type) { + case DESC_BOUND_T_FOR_SOLID: + case DESC_BOUND_T_FOR_FLUID: + case DESC_BOUND_H_FOR_SOLID: + case DESC_BOUND_H_FOR_FLUID: + FATAL("Cannot have a flux term here.\n"); break; + case DESC_BOUND_F_FOR_SOLID: { + double* w; + w = htable_weigth_find(&w_ctx->flux, &desc_id); + if(w) *w += flux_term; + else ERR(htable_weigth_set(&w_ctx->flux, &desc_id, &flux_term)); + break; + } + default: FATAL("Unreachable code.\n"); break; + } +end: + return res; +error: + goto end; +} + +static res_T +merge_power_terms + (struct sdis_medium* mdm, + const double power_term, + void* ctx) +{ + res_T res = RES_OK; + struct sdis_data* data = NULL; + enum sdis_medium_type type; + struct w_ctx* w_ctx = ctx; + + ASSERT(mdm && w_ctx); + + data = sdis_medium_get_data(mdm); + type = sdis_medium_get_type(mdm); + + switch (type) { + case SDIS_FLUID: { + /* Could be OK, but unimplemented in stardis */ + FATAL("Unexpected power term in fluid"); + } + case SDIS_SOLID: { + struct solid* d__ = sdis_data_get(data); + double* w; + unsigned id = d__->desc_id; + w = htable_weigth_find(&w_ctx->pw, &id); + if(w) *w += power_term; + else ERR(htable_weigth_set(&w_ctx->pw, &id, &power_term)); + break; + } + default: FATAL("Unreachable code.\n"); break; + } +end: + return res; +error: + goto end; +} + +/******************************************************************************* + * Public Functions + ******************************************************************************/ + +res_T +dump_path + (const struct sdis_heat_path* path, + void* context) +{ + res_T res = RES_OK; + struct dump_path_context* dump_ctx = context; + FILE* stream = NULL; + char* name = NULL; + enum sdis_heat_path_flag status = SDIS_HEAT_PATH_NONE; + size_t vcount_, name_sz; + unsigned long i, vcount; + + ASSERT(path && dump_ctx + && dump_ctx->stardis + && !str_is_empty(&dump_ctx->stardis->paths_filename)); + + ERR(sdis_heat_path_get_status(path, &status)); + + name_sz = 20 + str_len(&dump_ctx->stardis->paths_filename); + name = MEM_CALLOC(dump_ctx->stardis->allocator, name_sz, sizeof(*name)); + if(!name) { + res = RES_MEM_ERR; + goto error; + } + snprintf(name, name_sz, "%s%08lu%s.vtk", + str_cget(&dump_ctx->stardis->paths_filename), dump_ctx->rank++, + (status == SDIS_HEAT_PATH_FAILURE ? "_err" : "")); + + stream = fopen(name, "w"); + if(!stream) { + res = RES_IO_ERR; + goto error; + } + /* Header */ + fprintf(stream, "# vtk DataFile Version 2.0\n"); + fprintf(stream, "Heat path\n"); + fprintf(stream, "ASCII\n"); + fprintf(stream, "DATASET POLYDATA\n"); + /* Write path positions */ + sdis_heat_path_get_vertices_count(path, &vcount_); + ASSERT(vcount_ < ULONG_MAX); + vcount = (unsigned long)vcount_; + fprintf(stream, "POINTS %lu double\n", vcount); + FOR_EACH(i, 0, vcount) { + struct sdis_heat_vertex vtx; + ERR(sdis_heat_path_get_vertex(path, i, &vtx)); + fprintf(stream, "%g %g %g\n", SPLIT3(vtx.P)); + } + /* Write the segment of the path */ + fprintf(stream, "LINES %d %lu\n", 1, 1 + vcount); + fprintf(stream, "%lu", vcount); + FOR_EACH(i, 0, vcount) fprintf(stream, " %lu", i); + fprintf(stream, "\n"); + + /* Write path type */ + fprintf(stream, "CELL_DATA %d\n", 1); + fprintf(stream, "SCALARS Path_Type unsigned_char 1\n"); + fprintf(stream, "LOOKUP_TABLE default\n"); + switch (status) { + case SDIS_HEAT_PATH_SUCCESS: fprintf(stream, "0\n"); break; + case SDIS_HEAT_PATH_FAILURE: fprintf(stream, "1\n"); break; + default: FATAL("Unreachable code.\n"); break; + } + + fprintf(stream, "POINT_DATA %lu\n", vcount); + /* Write the type of the random walk vertices */ + fprintf(stream, "SCALARS Vertex_Type unsigned_char 1\n"); + fprintf(stream, "LOOKUP_TABLE default\n"); + FOR_EACH(i, 0, vcount) { + struct sdis_heat_vertex vtx; + ERR(sdis_heat_path_get_vertex(path, i, &vtx)); + ASSERT((size_t)vtx.type <= UCHAR_MAX); + fprintf(stream, "%d\n", vtx.type); + } + /* Write the weights of the random walk vertices */ + fprintf(stream, "SCALARS Weight double 1\n"); + fprintf(stream, "LOOKUP_TABLE default\n"); + FOR_EACH(i, 0, vcount) { + struct sdis_heat_vertex vtx; + ERR(sdis_heat_path_get_vertex(path, i, &vtx)); + fprintf(stream, "%g\n", vtx.weight); + } + /* If computation time is not INF for every vertex, + * write the time of the random walk vertices */ + FOR_EACH(i, 0, vcount) { + struct sdis_heat_vertex vtx; + ERR(sdis_heat_path_get_vertex(path, i, &vtx)); + if(i == 0) { + if(IS_INF(vtx.time)) break; + fprintf(stream, "SCALARS Time double 1\n"); + fprintf(stream, "LOOKUP_TABLE default\n"); + } + ASSERT(!IS_INF(vtx.time)); + fprintf(stream, "%g\n", vtx.time); + } + +end: + MEM_RM(dump_ctx->stardis->allocator, name); + if(stream) fclose(stream); + return res; +error: + goto end; +} + +res_T +dump_vtk_image + (const struct sdis_estimator_buffer* buf, + FILE* stream) +{ + res_T res = RES_OK; + size_t def[2]; + unsigned long definition[2]; + double* temps = NULL; + size_t ix, iy; + + ASSERT(buf && stream); + ERR(sdis_estimator_buffer_get_definition(buf, def)); + ASSERT(def[0] != 0 && def[1] != 0 && def[0] * def[1] <= ULONG_MAX); + definition[0] = (unsigned long)def[0]; + definition[1] = (unsigned long)def[1]; + + temps = mem_alloc(definition[0] * definition[1] * sizeof(double)); + if(temps == NULL) { + res = RES_MEM_ERR; + goto error; + } + + /* Compute the per pixel temperature */ + fprintf(stream, "# vtk DataFile Version 2.0\n"); + fprintf(stream, "Infrared Image\n"); + fprintf(stream, "ASCII\n"); + fprintf(stream, "DATASET STRUCTURED_POINTS\n"); + fprintf(stream, "DIMENSIONS %lu %lu 1\n", definition[0], definition[1]); + fprintf(stream, "ORIGIN 0 0 0\n"); + fprintf(stream, "SPACING 1 1 1\n"); + fprintf(stream, "POINT_DATA %lu\n", definition[0] * definition[1]); + fprintf(stream, "SCALARS temperature_estimate float 1\n"); + fprintf(stream, "LOOKUP_TABLE default\n"); + FOR_EACH(iy, 0, definition[1]) { + FOR_EACH(ix, 0, definition[0]) { + const struct sdis_estimator* estimator; + struct sdis_mc T; + ERR(sdis_estimator_buffer_at(buf, ix, iy, &estimator)); + ERR(sdis_estimator_get_temperature(estimator, &T)); + fprintf(stream, "%f\n", T.E); + } + } + fprintf(stream, "SCALARS temperature_std_dev float 1\n"); + fprintf(stream, "LOOKUP_TABLE default\n"); + FOR_EACH(iy, 0, definition[1]) { + FOR_EACH(ix, 0, definition[0]) { + const struct sdis_estimator* estimator; + struct sdis_mc T; + ERR(sdis_estimator_buffer_at(buf, ix, iy, &estimator)); + ERR(sdis_estimator_get_temperature(estimator, &T)); + fprintf(stream, "%f\n", T.SE); + } + } + fprintf(stream, "SCALARS computation_time float 1\n"); + fprintf(stream, "LOOKUP_TABLE default\n"); + FOR_EACH(iy, 0, definition[1]) { + FOR_EACH(ix, 0, definition[0]) { + const struct sdis_estimator* estimator; + struct sdis_mc time; + ERR(sdis_estimator_buffer_at(buf, ix, iy, &estimator)); + ERR(sdis_estimator_get_realisation_time(estimator, &time)); + fprintf(stream, "%f\n", time.E); + } + } + fprintf(stream, "SCALARS computation_time_std_dev float 1\n"); + fprintf(stream, "LOOKUP_TABLE default\n"); + FOR_EACH(iy, 0, definition[1]) { + FOR_EACH(ix, 0, definition[0]) { + const struct sdis_estimator* estimator; + struct sdis_mc time; + ERR(sdis_estimator_buffer_at(buf, ix, iy, &estimator)); + ERR(sdis_estimator_get_realisation_time(estimator, &time)); + fprintf(stream, "%f\n", time.SE); + } + } + fprintf(stream, "SCALARS failures_count unsigned_long_long 1\n"); + fprintf(stream, "LOOKUP_TABLE default\n"); + FOR_EACH(iy, 0, definition[1]) { + FOR_EACH(ix, 0, definition[0]) { + const struct sdis_estimator* estimator; + size_t nfails; + ERR(sdis_estimator_buffer_at(buf, ix, iy, &estimator)); + ERR(sdis_estimator_get_failure_count(estimator, &nfails)); + ASSERT(nfails <= ULONG_MAX); + fprintf(stream, "%lu\n", (unsigned long)nfails); + } + } + mem_rm(temps); + +end: + return res; +error: + goto end; +} + +res_T +dump_ht_image + (const struct sdis_estimator_buffer* buf, + FILE* stream) +{ + res_T res = RES_OK; + size_t def[2]; + unsigned long definition[2]; + size_t ix, iy; + + ASSERT(buf && stream); + ERR(sdis_estimator_buffer_get_definition(buf, def)); + ASSERT(def[0] <= ULONG_MAX && def[1] <= ULONG_MAX); + definition[0] = (unsigned long)def[0]; + definition[1] = (unsigned long)def[1]; + + fprintf(stream, "%lu %lu\n", definition[0], definition[1]); + FOR_EACH(iy, 0, definition[1]) { + FOR_EACH(ix, 0, definition[0]) { + const struct sdis_estimator* estimator; + struct sdis_mc T; + struct sdis_mc time; + ERR(sdis_estimator_buffer_at(buf, ix, iy, &estimator)); + ERR(sdis_estimator_get_realisation_time(estimator, &time)); + ERR(sdis_estimator_get_temperature(estimator, &T)); + fprintf(stream, "%f %f 0 0 0 0 %f %f\n", + T.E, T.SE, time.E, time.SE); + } + }; + +end: + return res; +error: + goto end; +} + +#define FW(Ptr, Count) \ + if((Count) != fwrite((Ptr), sizeof(*(Ptr)), (Count), stream)) { \ + res = RES_IO_ERR; \ + goto error; \ + } + +struct path_header { + unsigned id; + unsigned pcount, fcount; + char at_initial; +}; + +static FINLINE double +medium_get_t0 + (struct sdis_medium* medium) +{ + struct sdis_data* data = NULL; + enum sdis_medium_type type; + ASSERT(medium); + type = sdis_medium_get_type(medium); + data = sdis_medium_get_data(medium); + if(type == SDIS_FLUID) { + const struct fluid* fluid_props = sdis_data_cget(data); + return fluid_props->t0; + } else { + const struct solid* solid_props = sdis_data_cget(data); + ASSERT(type == SDIS_SOLID); + return solid_props->t0; + } +} + +static res_T +dump_sample_end + (struct sdis_green_path* path, + void* ctx) +{ + res_T res = RES_OK; + struct sdis_point pt = SDIS_POINT_NULL; + struct sdis_data* data = NULL; + enum sdis_medium_type type; + struct e_ctx* e_ctx = ctx; + const struct description* descs; + FILE* stream; + double elapsed; + double* pos; + unsigned id; + + CHK(path && ctx); + + stream = e_ctx->stream; + ERR(sdis_green_path_get_limit_point(path, &pt)); + ERR(sdis_green_path_get_elapsed_time(path, &elapsed)); + + descs = darray_descriptions_cdata_get(e_ctx->desc); + switch (pt.type) { + case SDIS_FRAGMENT: { + struct intface* d__; + data = sdis_interface_get_data(pt.data.itfrag.intface); + pos = pt.data.itfrag.fragment.P; + d__ = sdis_data_get(data); + id = d__->desc_id; + CHK(DESC_IS_T(descs[id].type) || DESC_IS_H(descs[id].type)); + break; + } + case SDIS_VERTEX: + type = sdis_medium_get_type(pt.data.mdmvert.medium); + data = sdis_medium_get_data(pt.data.mdmvert.medium); + pos = pt.data.mdmvert.vertex.P; + if(pt.data.mdmvert.vertex.P[0] == INF) { + /* Radiative output (ambient temperature) */ + size_t sz = darray_descriptions_size_get(e_ctx->desc); + ASSERT(sz <= UINT_MAX); + id = (unsigned)sz; /* Ambient ID */ + } + else if(type == SDIS_FLUID) { + struct fluid* d__ = sdis_data_get(data); + id = d__->desc_id; + } else { + struct solid* d__ = sdis_data_get(data); + ASSERT(type == SDIS_SOLID); + ASSERT(!d__->is_outside); /* FIXME: what if in external solid? */ + id = d__->desc_id; + } + break; + default: FATAL("Unreachable code.\n"); break; + } + + /* End, End ID, X, Y, Z, Elapsed time */ + if(pt.data.mdmvert.vertex.P[0] == INF) { + fprintf(stream, "AMBIANT, %u, 0, 0, 0, %g\n", + id, elapsed); + } else { + fprintf(stream, "%s, %u, %g, %g, %g, %g\n", + str_cget(get_description_name(descs + id)), id, SPLIT3(pos), elapsed); + } + +end: + return res; +error: + goto end; +} + +static res_T +dump_sample + (struct sdis_green_path* path, + void* ctx) +{ + res_T res = RES_OK; + struct sdis_point pt = SDIS_POINT_NULL; + struct sdis_data* data = NULL; + enum sdis_medium_type type; + struct htable_weigth_iterator it, end; + struct path_header header; + struct w_ctx* w_ctx = ctx; + const struct description* descs; + FILE* stream; + unsigned* ids = NULL; + double* weights = NULL; + size_t sz, i; + double t0; + + CHK(path && ctx); + + stream = w_ctx->stream; + ERR(sdis_green_path_get_limit_point(path, &pt)); + + /* For each path, dump: + * # end_id #power_terms #flux_terms + * power_id_1 ... power_id_n flux_id_1 ... flux_id_n + * power_factor_1 ... power_factor_n flux_factor_1 ... flux_factor_n + */ + + descs = darray_descriptions_cdata_get(w_ctx->desc); + switch (pt.type) { + case SDIS_FRAGMENT: { + struct intface* d__; + unsigned desc_id; + data = sdis_interface_get_data(pt.data.itfrag.intface); + d__ = sdis_data_get(data); + desc_id = d__->desc_id; + CHK(DESC_IS_T(descs[desc_id].type) || DESC_IS_H(descs[desc_id].type)); + header.id = desc_id; + header.at_initial = 0; + break; + } + case SDIS_VERTEX: + type = sdis_medium_get_type(pt.data.mdmvert.medium); + data = sdis_medium_get_data(pt.data.mdmvert.medium); + t0 = medium_get_t0(pt.data.mdmvert.medium); + header.at_initial = (pt.data.mdmvert.vertex.time <= t0); + if(pt.data.mdmvert.vertex.P[0] == INF) { + /* Radiative output (ambient temperature) */ + sz = darray_descriptions_size_get(w_ctx->desc); + ASSERT(sz <= UINT_MAX); + header.id = (unsigned)sz; /* Ambient ID */ + } + else if(type == SDIS_FLUID) { + struct fluid* d__ = sdis_data_get(data); + header.id = d__->desc_id; + } else { + struct solid* d__ = sdis_data_get(data); + ASSERT(type == SDIS_SOLID); + ASSERT(!d__->is_outside); /* FIXME: what if in external solid? */ + header.id = d__->desc_id; + } + break; + default: FATAL("Unreachable code.\n"); break; + } + + /* Merge power and flux terms */ + htable_weigth_clear(&w_ctx->pw); + htable_weigth_clear(&w_ctx->flux); + ERR(sdis_green_path_for_each_power_term(path, merge_power_terms, w_ctx)); + ERR(sdis_green_path_for_each_flux_term(path, merge_flux_terms, w_ctx)); + sz = htable_weigth_size_get(&w_ctx->pw); + ASSERT(sz <= UINT_MAX); + header.pcount = (unsigned)sz; + sz = htable_weigth_size_get(&w_ctx->flux); + ASSERT(sz <= UINT_MAX); + header.fcount = (unsigned)sz; + + /* Write path's header */ + FW(&header, 1); + + /* Allocate buffers */ + sz = header.pcount + header.fcount; + ids = MEM_CALLOC(w_ctx->alloc, sz, sizeof(*ids)); + weights = MEM_CALLOC(w_ctx->alloc, sz, sizeof(*weights)); + if(!ids || !weights) { + res = RES_MEM_ERR; + goto error; + } + + /* Write terms */ + htable_weigth_begin(&w_ctx->pw, &it); + htable_weigth_end(&w_ctx->pw, &end); + i = 0; + while(!htable_weigth_iterator_eq(&it, &end)) { + double* w = htable_weigth_iterator_data_get(&it); + unsigned* k = htable_weigth_iterator_key_get(&it); + ids[i] = *k; + weights[i] = *w; + htable_weigth_iterator_next(&it); + i++; + } + CHK(i == header.pcount); + + htable_weigth_begin(&w_ctx->flux, &it); + htable_weigth_end(&w_ctx->flux, &end); + while (!htable_weigth_iterator_eq(&it, &end)) { + double* w = htable_weigth_iterator_data_get(&it); + unsigned* k = htable_weigth_iterator_key_get(&it); + ids[i] = *k; + weights[i] = *w; + htable_weigth_iterator_next(&it); + i++; + } + CHK(i == header.pcount + header.fcount); + + FW(ids, sz); + FW(weights, sz); + +end: + MEM_RM(w_ctx->alloc, ids); + MEM_RM(w_ctx->alloc, weights); + return res; +error: + goto end; +} + +res_T +dump_green_bin + (struct sdis_green_function* green, + const struct stardis* stardis, + FILE* stream) +{ + res_T res = RES_OK; + size_t ok_count, failed_count; + size_t sz; + struct w_ctx w_ctx; + int table_initialized = 0; + unsigned i, szd; + const struct description* descs; + unsigned name_pool_sz = 0; + char* name_pool = NULL; + char* pool_ptr; + char green_string[] = "BINGREEN"; + + ASSERT(green && stardis && stream); + + ERR(sdis_green_function_get_paths_count(green, &ok_count)); + ERR(sdis_green_function_get_invalid_paths_count(green, &failed_count)); + sz = darray_descriptions_size_get(&stardis->descriptions); + ASSERT(sz <= UINT_MAX); + szd = (unsigned)sz; + descs = darray_descriptions_cdata_get(&stardis->descriptions); + + /* Save names that do not fit inplace */ + FOR_EACH(i, 0, szd) { + const struct description* desc = descs + i; + const struct str* name = get_description_name(desc); + const size_t len = str_len(name); + ASSERT(name_pool_sz + len + 1 <= UINT_MAX); + name_pool_sz += (unsigned)(len + 1); + } + pool_ptr = name_pool = MEM_ALLOC(stardis->allocator, name_pool_sz); + if(!name_pool) { + res = RES_MEM_ERR; + goto error; + } + FOR_EACH(i, 0, szd) { + const struct description* desc = descs + i; + const struct str* name = get_description_name(desc); + const size_t len = str_len(name); + strcpy(pool_ptr, name->cstr); + pool_ptr += len + 1; + } + ASSERT(pool_ptr == name_pool + name_pool_sz); + /* Write Green string */ + FW(green_string, sizeof(green_string)); + + /* Write counts */ + FW(&szd, 1); + FW(&stardis->counts, 1); + FW(&name_pool_sz, 1); + FW(&ok_count, 1); + FW(&failed_count, 1); + + /* Write descriptions*/ + FW(descs, szd); + + /* Write names */ + if(name_pool_sz) + FW(name_pool, name_pool_sz); + + /* Write radiative temperatures */ + FW(&stardis->ambient_temp, 1); + FW(&stardis->ref_temp, 1); + + /* Write time range */ + FW(&stardis->time_range, 2); + + w_ctx.alloc = stardis->allocator; + w_ctx.desc = &stardis->descriptions; + htable_weigth_init(NULL, &w_ctx.pw); + htable_weigth_init(NULL, &w_ctx.flux); + w_ctx.stream = stream; + table_initialized = 1; + + /* Write samples */ + ERR(sdis_green_function_for_each_path(green, dump_sample, &w_ctx)); + +end: + MEM_RM(stardis->allocator, name_pool); + if(table_initialized) htable_weigth_release(&w_ctx.pw); + if(table_initialized) htable_weigth_release(&w_ctx.flux); + return res; +error: + goto end; +} + +res_T +dump_paths_end + (struct sdis_green_function* green, + const struct stardis* stardis, + FILE* stream) +{ + res_T res = RES_OK; + struct e_ctx e_ctx = { 0 }; + + ASSERT(green && stardis && stream); + + e_ctx.desc = &stardis->descriptions; + e_ctx.stream = stream; + + fprintf(stream, "\"End\", \"End ID\", \"X\", \"Y\", \"Z\", \"Elapsed time\"\n"); + ERR(sdis_green_function_for_each_path(green, dump_sample_end, &e_ctx)); + +end: + return res; +error: + goto end; +} + +res_T +print_sample + (struct sdis_green_path* path, + void* ctx) +{ + res_T res = RES_OK; + struct sdis_point pt = SDIS_POINT_NULL; + struct sdis_data* data = NULL; + enum sdis_medium_type type; + struct htable_weigth_iterator it, end; + unsigned desc_id; + size_t pcount, fcount; + struct w_ctx* w_ctx = ctx; + const struct description* descs; + CHK(path && ctx); + + ERR(sdis_green_path_get_limit_point(path, &pt)); + + /* For each path, prints: + * # end #power_terms #flux_terms power_term_1 ... power_term_n flux_term_1 ... flux_term_n + * with: + * - end = end_type end_id; end_type = T | H | A | F | S + * - power_term_i = power_type_i power_id_i factor_i + * - flux_term_i = flux_id_i factor_i + */ + + descs = darray_descriptions_cdata_get(w_ctx->desc); + switch (pt.type) { + case SDIS_FRAGMENT: { + struct intface* d__; + data = sdis_interface_get_data(pt.data.itfrag.intface); + d__ = sdis_data_get(data); + desc_id = d__->desc_id; + switch (descs[desc_id].type) { + case DESC_BOUND_T_FOR_SOLID: + case DESC_BOUND_T_FOR_FLUID: + fprintf(w_ctx->stream, "T\t%u", desc_id); + break; + case DESC_BOUND_H_FOR_SOLID: + case DESC_BOUND_H_FOR_FLUID: + fprintf(w_ctx->stream, "H\t%u", desc_id); + break; + case DESC_BOUND_F_FOR_SOLID: + FATAL("Heat path cannot end at a flux boundary.\n"); break; + break; + default: FATAL("Unreachable code.\n"); break; + } + break; + } + case SDIS_VERTEX: + type = sdis_medium_get_type(pt.data.mdmvert.medium); + data = sdis_medium_get_data(pt.data.mdmvert.medium); + if(pt.data.mdmvert.vertex.P[0] == INF) { + /* Radiative output (ambient temperature)*/ + size_t sz = darray_descriptions_size_get(w_ctx->desc); + ASSERT(sz <= UINT_MAX); + fprintf(w_ctx->stream, "A\t%u", (unsigned)sz); + } + else if(type == SDIS_FLUID) { + struct fluid* d__ = sdis_data_get(data); + desc_id = d__->desc_id; + if(d__->is_outside) + /* If outside the model and in a fluid with known temperature, + * its a fluid attached to a H boundary */ + fprintf(w_ctx->stream, "H\t%u", desc_id); + /* In a standard fluid with known temperature */ + else fprintf(w_ctx->stream, "F\t%u", desc_id); + } else { + struct solid* d__ = sdis_data_get(data); + ASSERT(type == SDIS_SOLID); + ASSERT(!d__->is_outside); /* FIXME: what if in external solid? */ + desc_id = d__->desc_id; + fprintf(w_ctx->stream, "S\t%u", desc_id); + } + break; + default: FATAL("Unreachable code.\n"); break; + } + + ERR(sdis_green_function_get_power_terms_count(path, &pcount)); + + htable_weigth_clear(&w_ctx->pw); + htable_weigth_clear(&w_ctx->flux); + ERR(sdis_green_path_for_each_power_term(path, merge_power_terms, w_ctx)); + ERR(sdis_green_path_for_each_flux_term(path, merge_flux_terms, w_ctx)); + fcount = htable_weigth_size_get(&w_ctx->flux); + + ASSERT(pcount <= ULONG_MAX && fcount <= ULONG_MAX); + fprintf(w_ctx->stream, "\t%lu\t%lu", + (unsigned long)pcount, (unsigned long)fcount); + + htable_weigth_begin(&w_ctx->pw, &it); + htable_weigth_end(&w_ctx->pw, &end); + while(!htable_weigth_iterator_eq(&it, &end)) { + double* w = htable_weigth_iterator_data_get(&it); + unsigned* k = htable_weigth_iterator_key_get(&it); + fprintf(w_ctx->stream, "\t%u\t%g", *k, *w); + htable_weigth_iterator_next(&it); + } + + htable_weigth_begin(&w_ctx->flux, &it); + htable_weigth_end(&w_ctx->flux, &end); + while (!htable_weigth_iterator_eq(&it, &end)) { + double* w = htable_weigth_iterator_data_get(&it); + unsigned* k = htable_weigth_iterator_key_get(&it); + fprintf(w_ctx->stream, "\t%u\t%g", *k, *w); + htable_weigth_iterator_next(&it); + } + fprintf(w_ctx->stream, "\n"); + +end: + return res; +error: + goto end; +} + +res_T +dump_green_ascii + (struct sdis_green_function* green, + const struct stardis* stardis, + FILE* stream) +{ + res_T res = RES_OK; + unsigned ok_count, failed_count; + size_t sz; + struct w_ctx w_ctx; + int table_initialized = 0; + unsigned i, szd; + const struct description* descs; + + ASSERT(green && stardis && stream); + + ERR(sdis_green_function_get_paths_count(green, &sz)); + ASSERT(sz <= UINT_MAX); + ok_count = (unsigned)sz; + ERR(sdis_green_function_get_invalid_paths_count(green, &sz)); + ASSERT(sz <= UINT_MAX); + failed_count = (unsigned)sz; + sz = darray_descriptions_size_get(&stardis->descriptions); + ASSERT(sz <= UINT_MAX); + szd = (unsigned)sz; + descs = darray_descriptions_cdata_get(&stardis->descriptions); + + /* Output counts */ + fprintf(stream, "---BEGIN GREEN---\n"); + fprintf(stream, "# time range\n"); + fprintf(stream, "%g %g\n", + SPLIT2(stardis->time_range)); + fprintf(stream, + "# #solids #fluids #t_boundaries #h_boundaries #f_boundaries #ok #failures\n"); + fprintf(stream, "%u %u %u %u %u %u %u\n", + stardis->counts.smed_count, stardis->counts.fmed_count, + stardis->counts.tbound_count, stardis->counts.hbound_count, + stardis->counts.fbound_count, ok_count, failed_count); + + /* List Media */ + if(stardis->counts.smed_count) { + fprintf(stream, "# Solids\n"); + fprintf(stream, "# ID Name lambda rho cp power initial_temp imposed_temp\n"); + FOR_EACH(i, 0, szd) { + const struct description* desc = descs + i; + const struct solid* sl; + if(desc->type != DESC_MAT_SOLID) continue; + sl = &desc->d.solid; + fprintf(stream, "%u\t%s\t%g\t%g\t%g\t%g", + i, str_cget(&sl->name), sl->lambda, sl->rho, sl->cp, sl->vpower); + if(sl->tinit >= 0) { + fprintf(stream, "\t%g", sl->tinit); + } else { + fprintf(stream, "\tNONE"); + } + if(sl->imposed_temperature >= 0) { + fprintf(stream, "\t%g\n", sl->imposed_temperature); + } else { + fprintf(stream, "\tNONE\n"); + } + } + } + if(stardis->counts.fmed_count) { + fprintf(stream, "# Fluids\n"); + fprintf(stream, "# ID Name rho cp initial_temp imposed_temp\n"); + FOR_EACH(i, 0, szd) { + const struct description* desc = descs + i; + const struct fluid* fl; + if(desc->type != DESC_MAT_FLUID) continue; + fl = &desc->d.fluid; + if(fl->imposed_temperature >= 0) { + fprintf(stream, "%u\t%s\t%g\t%g", + i, str_cget(&fl->name), fl->rho, fl->cp); + } else { + fprintf(stream, "%u\t%s\t%g\t%g", + i, str_cget(&fl->name), fl->rho, fl->cp); + } + if(fl->tinit >= 0) { + fprintf(stream, "\t%g", fl->tinit); + } else { + fprintf(stream, "\tNONE"); + } + if(fl->imposed_temperature >= 0) { + fprintf(stream, "\t%g\n", fl->imposed_temperature); + } else { + fprintf(stream, "\tNONE\n"); + } + } + } + + /* List Boundaries */ + if(stardis->counts.tbound_count) { + fprintf(stream, "# T Boundaries\n"); + fprintf(stream, "# ID Name temperature\n"); + FOR_EACH(i, 0, szd) { + const struct description* desc = descs + i; + const struct t_boundary* bd; + if(desc->type != DESC_BOUND_T_FOR_SOLID + && desc->type != DESC_BOUND_T_FOR_FLUID) continue; + bd = &desc->d.t_boundary; + fprintf(stream, "%u\t%s\t%g\n", + i, str_cget(&bd->name), bd->imposed_temperature); + } + } + if(stardis->counts.hbound_count) { + fprintf(stream, "# H Boundaries\n"); + fprintf(stream, "# ID Name emissivity specular_fraction hc T_env\n"); + FOR_EACH(i, 0, szd) { + const struct description* desc = descs + i; + const struct h_boundary* bd; + if(desc->type != DESC_BOUND_H_FOR_SOLID + && desc->type != DESC_BOUND_H_FOR_FLUID) continue; + bd = &desc->d.h_boundary; + fprintf(stream, "%u\t%s\t%g\t%g\t%g\t%g\n", + i, str_cget(&bd->name), bd->emissivity, bd->specular_fraction, + bd->hc, bd->imposed_temperature); + } + } + if(stardis->counts.fbound_count) { + fprintf(stream, "# F Boundaries\n"); + fprintf(stream, "# ID Name flux\n"); + FOR_EACH(i, 0, szd) { + const struct description* desc = descs + i; + const struct f_boundary* bd; + if(desc->type != DESC_BOUND_F_FOR_SOLID) continue; + bd = &desc->d.f_boundary; + fprintf(stream, "%u\t%s\t%g\n", + i, str_cget(&bd->name), bd->imposed_flux); + } + } + + /* Radiative Temperatures */ + fprintf(stream, "# Radiative Temperatures\n"); + fprintf(stream, "# ID Amb_Temp Lin_Temp\n"); + fprintf(stream, "%u\t%g\t%g\n", + szd, stardis->ambient_temp, stardis->ref_temp); + + fprintf(stream, "# Samples\n"); + fprintf(stream, + "# end #power_terms #flux_terms power_term_1 ... power_term_n flux_term_1 ... flux_term_n\n"); + fprintf(stream, "# end = end_type end_id; end_type = T | H | A | F | S\n"); + fprintf(stream, "# power_term_i = power_id_i factor_i\n"); + fprintf(stream, "# flux_term_i = flux_id_i factor_i\n"); + + w_ctx.alloc = stardis->allocator; + w_ctx.desc = &stardis->descriptions; + htable_weigth_init(NULL, &w_ctx.pw); + htable_weigth_init(NULL, &w_ctx.flux); + w_ctx.stream = stream; + table_initialized = 1; + + ERR(sdis_green_function_for_each_path(green, print_sample, &w_ctx)); + + fprintf(stream, "---END GREEN---\n"); + +end: + if(table_initialized) htable_weigth_release(&w_ctx.pw); + if(table_initialized) htable_weigth_release(&w_ctx.flux); + return res; +error: + goto end; +} + +res_T +dump_boundaries_at_the_end_of_vtk + (const struct stardis* stardis, + FILE* stream) +{ + res_T res = RES_OK; + const struct description* descriptions; + unsigned tsz, t; + ASSERT(stardis && stream); + + ERR(sg3d_geometry_get_unique_triangles_count(stardis->geometry.sg3d, &tsz)); + descriptions = darray_descriptions_cdata_get(&stardis->descriptions); + + fprintf(stream, "SCALARS Boundaries unsigned_int 1\n"); + fprintf(stream, "LOOKUP_TABLE default\n"); + FOR_EACH(t, 0, tsz) { + unsigned descr[SG3D_PROP_TYPES_COUNT__]; + ERR(sg3d_geometry_get_unique_triangle_properties(stardis->geometry.sg3d, t, + descr)); + if(descr[SG3D_INTFACE] != SG3D_UNSPECIFIED_PROPERTY + && DESC_IS_BOUNDARY(descriptions[descr[SG3D_INTFACE]].type)) + /* Descriptions are numbered from 1 in the log (so the 1+ below) */ + fprintf(stream, "%u\n", 1 + descr[SG3D_INTFACE]); + else fprintf(stream, "%u\n", SG3D_UNSPECIFIED_PROPERTY); + } + +exit: + return res; +error: + goto exit; +} + +res_T +dump_enclosure_related_stuff_at_the_end_of_vtk + (struct stardis* stardis, + FILE* stream) +{ + res_T res = RES_OK; + unsigned* trgs = NULL; + struct senc3d_enclosure* enc = NULL; + unsigned tsz, e, s, t, scount, ecount, ocount; + int* enc_status = NULL; + const struct description* descriptions; + int invalid_enclosures_count = 0; + ASSERT(stardis && stream); + + descriptions = darray_descriptions_cdata_get(&stardis->descriptions); + ERR(sg3d_geometry_get_unique_triangles_count(stardis->geometry.sg3d, &tsz)); + trgs = MEM_CALLOC(stardis->allocator, tsz, sizeof(*trgs)); + if(!trgs) { + res = RES_MEM_ERR; + goto error; + } + + /* If enclosure where not extracted, dump only errors */ + ERR(senc3d_scene_get_overlapping_triangles_count(stardis->senc3d_scn, &ocount)); + if(ocount) { + FOR_EACH(t, 0, tsz) trgs[t] = 0; + FOR_EACH(t, 0, ocount) { + unsigned trid; + ERR(senc3d_scene_get_overlapping_triangle(stardis->senc3d_scn, t, &trid)); + trgs[trid] = 1; + } + fprintf(stream, "SCALARS Overlapping_triangles unsigned_int 1\n"); + fprintf(stream, "LOOKUP_TABLE default\n"); + FOR_EACH(t, 0, tsz) fprintf(stream, "%u\n", trgs[t]); + goto exit; + } + + /* Keep the segments involved in holes (not the vertices) */ + ERR(senc3d_scene_get_frontier_segments_count(stardis->senc3d_scn, &scount)); + if(scount) { + /* Room to store frontier triangles */ + FOR_EACH(s, 0, scount) { + unsigned vrtc[2], trid; + ERR(senc3d_scene_get_frontier_segment(stardis->senc3d_scn, s, vrtc, &trid)); + trgs[trid] = 1; + } + logger_print(stardis->logger, LOG_WARNING, "Model contains hole(s).\n"); + fprintf(stream, "SCALARS Hole_frontiers unsigned_int 1\n"); + fprintf(stream, "LOOKUP_TABLE default\n"); + FOR_EACH(t, 0, tsz) fprintf(stream, "%u\n", trgs[t]); + } + + /* Dump enclosure information */ + ERR(senc3d_scene_get_enclosure_count(stardis->senc3d_scn, &ecount)); + enc_status = MEM_CALLOC(stardis->allocator, ecount, sizeof(*enc_status)); + if(!enc_status) { + res = RES_MEM_ERR; + goto error; + } + ASSERT(stardis->undefined_medium_behind_boundary_id != SENC3D_UNSPECIFIED_MEDIUM); + FOR_EACH(e, 0, ecount) { + struct senc3d_enclosure_header header; + unsigned tid, med, enc_fst_med = SENC3D_UNSPECIFIED_MEDIUM; + int is_fst_med = 1, is_err_cs = 0; + + enc_status[e] = NO_ENCLOSURE_ERROR; + ERR(senc3d_scene_get_enclosure(stardis->senc3d_scn, e, &enc)); + ERR(senc3d_enclosure_get_header(enc, &header)); + + FOR_EACH(t, 0, header.unique_primitives_count) { + unsigned prop[SG3D_PROP_TYPES_COUNT__]; + enum senc3d_side side; + size_t j; + ERR(senc3d_enclosure_get_triangle_id(enc, t, &tid, &side)); + FOR_EACH(j, 0, darray_uint_size_get(&stardis->compute_surface.err_triangles)) + { + unsigned prim + = darray_uint_cdata_get(&stardis->compute_surface.err_triangles)[j]; + if(prim == tid) { + is_err_cs = 1; + break; + } + } + if(is_err_cs) + /* Don't flag an enclosure invalid because of a triangle that is + * considered not member of it */ + continue; + + ERR(sg3d_geometry_get_unique_triangle_properties(stardis->geometry.sg3d, + tid, prop)); + + if(prop[side] != SG3D_UNSPECIFIED_PROPERTY) { + ASSERT(prop[side] < darray_descriptions_size_get(&stardis->descriptions)); + description_get_medium_id(descriptions + prop[side], &med); + } else { + /* If unspecified behind a boundary, use a specific ID to avoid to flag + * using different boundaries for a given enclosure as invalid */ + int properties_conflict_status; + validate_properties(t, prop, stardis, &properties_conflict_status); + if(properties_conflict_status == NO_PROPERTY_CONFLICT) + med = stardis->undefined_medium_behind_boundary_id; + else { + enc_status[e] |= ENCLOSURE_WITH_UNDEF_MEDIUM; + continue; /* Don't flag N_MEDIA at the same time */ + } + } + if(is_fst_med) { + is_fst_med = 0; + enc_fst_med = med; + } else { + if(enc_fst_med != med) + enc_status[e] |= ENCLOSURE_WITH_N_MEDIA; + } + } + /* The external (infinite) enclosure is always valid */ + if(enc_status[e] != NO_ENCLOSURE_ERROR && !header.is_infinite) + invalid_enclosures_count++; + ERR(senc3d_enclosure_ref_put(enc)); + } + if(invalid_enclosures_count) { + logger_print(stardis->logger, LOG_WARNING, + "Found %d invalid enclosure(s).\n", invalid_enclosures_count); + } + fprintf(stream, "FIELD EnclosuresData 2\n"); + fprintf(stream, "Enclosures %d %d unsigned_char\n", ecount, tsz); + FOR_EACH(t, 0, tsz) { + unsigned encs[2], is_err_cs = 0; + size_t j; + FOR_EACH(j, 0, darray_uint_size_get(&stardis->compute_surface.err_triangles)) + { + unsigned prim + = darray_uint_cdata_get(&stardis->compute_surface.err_triangles)[j]; + if(prim == t) { + is_err_cs = 1; + break; + } + } + if(is_err_cs) { + /* Triangles in compute surface with error are considered member of no + * enclosure */ + FOR_EACH(e, 1, ecount) fprintf(stream, "0 "); + fprintf(stream, "0\n"); + continue; + } + ERR(senc3d_scene_get_triangle_enclosures(stardis->senc3d_scn, t, encs)); + FOR_EACH(e, 0, ecount) { + unsigned c = (e == encs[SENC3D_FRONT] || e == encs[SENC3D_BACK]) + ? (unsigned char)enc_status[e] : 0; + if(e == ecount - 1) + fprintf(stream, "%u\n", c); + else fprintf(stream, "%u ", c); + } + } + +#define ENC_NOT_MEMBER SENC3D_UNSPECIFIED_MEDIUM +#define ENC_MEMBER_2_DISTINT_MEDIA (ENC_NOT_MEMBER - 1) +#define ENC_MEMBER_NO_MEDIUM (ENC_NOT_MEMBER - 2) + + fprintf(stream, "Enclosures_internal_media %d %d unsigned_int\n", ecount, tsz); + FOR_EACH(t, 0, tsz) { + unsigned descr[SG3D_PROP_TYPES_COUNT__]; + unsigned encs[2]; + unsigned is_err_cs = 0; + size_t j; + ERR(senc3d_scene_get_triangle_enclosures(stardis->senc3d_scn, t, encs)); + ERR(sg3d_geometry_get_unique_triangle_properties(stardis->geometry.sg3d, t, + descr)); + + /* Special value for triangles in compute surface with error */ + FOR_EACH(j, 0, darray_uint_size_get(&stardis->compute_surface.err_triangles)) + { + unsigned prim + = darray_uint_cdata_get(&stardis->compute_surface.err_triangles)[j]; + if(prim == t) { + is_err_cs = 1; + break; + } + } + if(is_err_cs) { + /* Triangles in compute surface with error are considered member of no + * enclosure */ + FOR_EACH(e, 1, ecount) fprintf(stream, "%u ", ENC_NOT_MEMBER); + fprintf(stream, "%u\n", ENC_NOT_MEMBER); + continue; + } + FOR_EACH(e, 0, ecount) { + unsigned mid; + if(e == encs[SENC3D_FRONT] && e == encs[SENC3D_BACK]) { + /* Both sides of this triangle are in enclosure #e */ + unsigned fmid, bmid; + if(descr[SG3D_FRONT] != SG3D_UNSPECIFIED_PROPERTY) + description_get_medium_id(descriptions + descr[SG3D_FRONT], &fmid); + else if(descr[SG3D_INTFACE] != SG3D_UNSPECIFIED_PROPERTY + && DESC_IS_BOUNDARY(descriptions[descr[SG3D_INTFACE]].type)) + { + description_get_medium_id(descriptions + descr[SG3D_INTFACE], &fmid); + } + else fmid = ENC_MEMBER_NO_MEDIUM; + if(descr[SENC3D_BACK] != SG3D_UNSPECIFIED_PROPERTY) + description_get_medium_id(descriptions + descr[SENC3D_BACK], &bmid); + else if(descr[SG3D_INTFACE] != SG3D_UNSPECIFIED_PROPERTY + && DESC_IS_BOUNDARY(descriptions[descr[SG3D_INTFACE]].type)) + { + description_get_medium_id(descriptions + descr[SG3D_INTFACE], &bmid); + } + else bmid = ENC_MEMBER_NO_MEDIUM; + mid = (fmid == bmid) ? fmid : ENC_MEMBER_2_DISTINT_MEDIA; + } + else if(e == encs[SENC3D_FRONT]) { + /* Member of enclosure #e (front side only) */ + if(descr[SG3D_FRONT] != SG3D_UNSPECIFIED_PROPERTY) + description_get_medium_id(descriptions + descr[SG3D_FRONT], &mid); + else if(descr[SG3D_INTFACE] != SG3D_UNSPECIFIED_PROPERTY + && DESC_IS_BOUNDARY(descriptions[descr[SG3D_INTFACE]].type)) + { + description_get_medium_id(descriptions + descr[SG3D_INTFACE], &mid); + } + else mid = ENC_MEMBER_NO_MEDIUM; + } + else if(e == encs[SENC3D_BACK]) { + /* Member of enclosure #e (back side only) */ + if(descr[SENC3D_BACK] != SG3D_UNSPECIFIED_PROPERTY) + description_get_medium_id(descriptions + descr[SENC3D_BACK], &mid); + else if(descr[SG3D_INTFACE] != SG3D_UNSPECIFIED_PROPERTY + && DESC_IS_BOUNDARY(descriptions[descr[SG3D_INTFACE]].type)) + { + description_get_medium_id(descriptions + descr[SG3D_INTFACE], &mid); + } + else mid = ENC_MEMBER_NO_MEDIUM; + } else { + /* Not member of enclosure #e */ + mid = ENC_NOT_MEMBER; + } + if(e == ecount - 1) + fprintf(stream, "%u\n", mid); + else fprintf(stream, "%u ", mid); + } + } + +#undef ENC_NOT_MEMBER +#undef ENC_ERR_COMPUTE_SURFACE +#undef ENC_MEMBER_2_DISTINT_MEDIA +#undef ENC_MEMBER_NO_MEDIUM + +exit: + MEM_RM(stardis->allocator, trgs); + MEM_RM(stardis->allocator, enc_status); + return res; +error: + goto exit; +} + +res_T +print_single_MC_result + (struct sdis_estimator* estimator, + struct stardis* stardis, + FILE* stream) +{ + res_T res = RES_OK; + struct sdis_mc result; + size_t nfailures_; + unsigned long nfailures, nsamples; + + ASSERT(estimator && stardis && stream); + + /* Fetch the estimation data */ + ERR(sdis_estimator_get_temperature(estimator, &result)); + ERR(sdis_estimator_get_failure_count(estimator, &nfailures_)); + ASSERT(nfailures_ <= ULONG_MAX && stardis->samples <= ULONG_MAX); + nfailures = (unsigned long)nfailures_; + nsamples = (unsigned long)stardis->samples; + if(nfailures == nsamples) { + logger_print(stardis->logger, LOG_ERROR, + "All the %lu samples failed. No result to display.\n", nsamples); + res = RES_BAD_OP; + goto error; + } + + /* Print the results */ + switch (stardis->mode & COMPUTE_MODES) { + case MODE_PROBE_COMPUTE: + if(stardis->mode & MODE_EXTENDED_RESULTS) { + if(stardis->time_range[0] == stardis->time_range[1]) + fprintf(stream, "Temperature at [%g, %g, %g] at t=%g = %g +/- %g\n", + SPLIT3(stardis->probe), stardis->time_range[0], + result.E, /* Expected value */ + result.SE); /* Standard error */ + else + fprintf(stream, "Temperature at [%g, %g, %g] with t in [%g %g] = %g +/- %g\n", + SPLIT3(stardis->probe), SPLIT2(stardis->time_range), + result.E, /* Expected value */ + result.SE); /* Standard error */ + } + else fprintf(stream, "%g %g %lu %lu\n", + result.E, result.SE, nfailures, nsamples); + break; + case MODE_PROBE_COMPUTE_ON_INTERFACE: + if(stardis->mode & MODE_EXTENDED_RESULTS) { + if(stardis->time_range[0] == stardis->time_range[1]) + fprintf(stream, "Boundary temperature at [%g, %g, %g] at t=%g = %g +/- %g\n", + SPLIT3(stardis->probe), stardis->time_range[0], + result.E, /* Expected value */ + result.SE); /* Standard error */ + else + fprintf(stream, "Boundary temperature at [%g, %g, %g] with t in [%g %g] = %g +/- %g\n", + SPLIT3(stardis->probe), SPLIT2(stardis->time_range), + result.E, /* Expected value */ + result.SE); /* Standard error */ + } + else fprintf(stream, "%g %g %lu %lu\n", + result.E, result.SE, nfailures, nsamples); + break; + case MODE_MEDIUM_COMPUTE: + if(stardis->mode & MODE_EXTENDED_RESULTS) { + if(stardis->time_range[0] == stardis->time_range[1]) + fprintf(stream, "Temperature in medium '%s' at t=%g = %g +/- %g\n", + str_cget(&stardis->solve_name), stardis->time_range[0], + result.E, /* Expected value */ + result.SE); /* Standard error */ + else + fprintf(stream, "Temperature in medium '%s' with t in [%g %g] = %g +/- %g\n", + str_cget(&stardis->solve_name), SPLIT2(stardis->time_range), + result.E, /* Expected value */ + result.SE); /* Standard error */ + } + else fprintf(stream, "%g %g %lu %lu\n", + result.E, result.SE, nfailures, nsamples); + break; + case MODE_BOUNDARY_COMPUTE: + if(stardis->mode & MODE_EXTENDED_RESULTS) { + if(stardis->time_range[0] == stardis->time_range[1]) + fprintf(stream, "Temperature at boundary '%s' at t=%g = %g +/- %g\n", + str_cget(&stardis->solve_name), stardis->time_range[0], + result.E, /* Expected value */ + result.SE); /* Standard error */ + else + fprintf(stream, "Temperature at boundary '%s' with t in [%g %g] = %g +/- %g\n", + str_cget(&stardis->solve_name), SPLIT2(stardis->time_range), + result.E, /* Expected value */ + result.SE); /* Standard error */ + } + else fprintf(stream, "%g %g %lu %lu\n", + result.E, result.SE, nfailures, nsamples); + break; + case MODE_FLUX_BOUNDARY_COMPUTE: { + enum sdis_estimator_type type; + ERR(sdis_estimator_get_type(estimator, &type)); + ASSERT(type == SDIS_ESTIMATOR_FLUX); + + if(stardis->mode & MODE_EXTENDED_RESULTS) { + if(stardis->time_range[0] == stardis->time_range[1]) + fprintf(stream, "Temperature at boundary '%s' at t=%g = %g +/- %g\n", + str_cget(&stardis->solve_name), stardis->time_range[0], + result.E, /* Expected value */ + result.SE); /* Standard error */ + else + fprintf(stream, "Temperature at boundary '%s' with t in [%g %g] = %g +/- %g\n", + str_cget(&stardis->solve_name), SPLIT2(stardis->time_range), + result.E, /* Expected value */ + result.SE); /* Standard error */ + ERR(sdis_estimator_get_convective_flux(estimator, &result)); + if(stardis->time_range[0] == stardis->time_range[1]) + fprintf(stream, "Convective flux at boundary '%s' at t=%g = %g +/- %g\n", + str_cget(&stardis->solve_name), stardis->time_range[0], + result.E, /* Expected value */ + result.SE); /* Standard error */ + else + fprintf(stream, "Convective flux at boundary '%s' with t in [%g %g] = %g +/- %g\n", + str_cget(&stardis->solve_name), SPLIT2(stardis->time_range), + result.E, /* Expected value */ + result.SE); /* Standard error */ + ERR(sdis_estimator_get_radiative_flux(estimator, &result)); + if(stardis->time_range[0] == stardis->time_range[1]) + fprintf(stream, "Radiative flux at boundary '%s' at t=%g = %g +/- %g\n", + str_cget(&stardis->solve_name), stardis->time_range[0], + result.E, /* Expected value */ + result.SE); /* Standard error */ + else + fprintf(stream, "Radiative flux at boundary '%s' with t in [%g %g] = %g +/- %g\n", + str_cget(&stardis->solve_name), SPLIT2(stardis->time_range), + result.E, /* Expected value */ + result.SE); /* Standard error */ + ERR(sdis_estimator_get_imposed_flux(estimator, &result)); + if(stardis->time_range[0] == stardis->time_range[1]) + fprintf(stream, "Imposed flux at boundary '%s' at t=%g = %g +/- %g\n", + str_cget(&stardis->solve_name), stardis->time_range[0], + result.E, /* Expected value */ + result.SE); /* Standard error */ + else + fprintf(stream, "Imposed flux at boundary '%s' with t in [%g %g] = %g +/- %g\n", + str_cget(&stardis->solve_name), SPLIT2(stardis->time_range), + result.E, /* Expected value */ + result.SE); /* Standard error */ + ERR(sdis_estimator_get_total_flux(estimator, &result)); + if(stardis->time_range[0] == stardis->time_range[1]) + fprintf(stream, "Total flux Flux at boundary '%s' at t=%g = %g +/- %g\n", + str_cget(&stardis->solve_name), stardis->time_range[0], + result.E, /* Expected value */ + result.SE); /* Standard error */ + else + fprintf(stream, "Total flux Flux at boundary '%s' with t in [%g %g] = %g +/- %g\n", + str_cget(&stardis->solve_name), SPLIT2(stardis->time_range), + result.E, /* Expected value */ + result.SE); /* Standard error */ + } else { + fprintf(stream, "%g %g ", result.E, result.SE); + ERR(sdis_estimator_get_convective_flux(estimator, &result)); + fprintf(stream, "%g %g ", result.E, result.SE); + ERR(sdis_estimator_get_radiative_flux(estimator, &result)); + fprintf(stream, "%g %g ", result.E, result.SE); + ERR(sdis_estimator_get_imposed_flux(estimator, &result)); + fprintf(stream, "%g %g ", result.E, result.SE); + ERR(sdis_estimator_get_total_flux(estimator, &result)); + fprintf(stream, "%g %g ", result.E, result.SE); + fprintf(stream, "%lu %lu\n", nfailures, nsamples); + } + break; + } + default: FATAL("Invalid mode."); + } + if(stardis->mode & MODE_EXTENDED_RESULTS) + fprintf(stream, "#failures: %lu/%lu\n", nfailures, nsamples); + if(nfailures) + logger_print(stardis->logger, LOG_ERROR, + "#failures: %lu/%lu\n", nfailures, nsamples); + +end: + return res; +error: + goto end; +} + +void +dump_map + (const struct stardis* stardis, + const struct darray_estimators* estimators, + FILE* stream) +{ + unsigned i, vcount, tcount, last_v = 0; + const size_t* idx; + size_t sz; + unsigned szp; + struct sdis_estimator* const* est; + + ASSERT(stardis && estimators && stream); + + est = darray_estimators_cdata_get(estimators); + idx = darray_size_t_cdata_get(&stardis->compute_surface.primitives); + sz = darray_size_t_size_get(&stardis->compute_surface.primitives); + ASSERT(sz <= UINT_MAX); + szp = (unsigned)sz; + SG3D(geometry_get_unique_vertices_count(stardis->geometry.sg3d, &vcount)); + SG3D(geometry_get_unique_triangles_count(stardis->geometry.sg3d, &tcount)); + + /* Find last used vertex */ + for(i = 0; i < szp; ++i) { + unsigned t; + unsigned indices[3]; + ASSERT(idx[i] <= UINT_MAX); + t = (unsigned)idx[i]; + SG3D(geometry_get_unique_triangle_vertices(stardis->geometry.sg3d, t, + indices)); + last_v = MMAX(MMAX(last_v, indices[0]), MMAX(indices[1], indices[2])); + } + + /* Dump vertices up to last_v, even unused ones, to avoid reindexing */ + fprintf(stream, "# vtk DataFile Version 2.0\n"); + fprintf(stream, "Temperature Map\n"); + fprintf(stream, "ASCII\n"); + fprintf(stream, "DATASET POLYDATA\n"); + fprintf(stream, "POINTS %u float\n\n", last_v + 1); + for(i = 0; i <= last_v; ++i) { + double coord[3]; + SG3D(geometry_get_unique_vertex(stardis->geometry.sg3d, i, coord)); + fprintf(stream, "%f %f %f\n", SPLIT3(coord)); + } + /* Dump only primitives in boundary */ + fprintf(stream, "\nPOLYGONS %u %u\n", szp, 4 * szp); + for(i = 0; i < szp; ++i) { + unsigned t; + unsigned indices[3]; + ASSERT(idx[i] <= UINT_MAX); + t = (unsigned)idx[i]; + SG3D(geometry_get_unique_triangle_vertices(stardis->geometry.sg3d, t, + indices)); + fprintf(stream, "3 %u %u %u\n", SPLIT3(indices)); + } + fprintf(stream, "\nCELL_DATA %u\n", szp); + fprintf(stream, "SCALARS temperature_estimate float 1\n"); + fprintf(stream, "LOOKUP_TABLE default\n"); + for(i = 0; i < szp; ++i) { + struct sdis_mc T; + SDIS(estimator_get_temperature(est[i], &T)); + fprintf(stream, "%f\n", T.E); + } + fprintf(stream, "SCALARS temperature_std_dev float 1\n"); + fprintf(stream, "LOOKUP_TABLE default\n"); + for(i = 0; i < szp; ++i) { + struct sdis_mc T; + SDIS(estimator_get_temperature(est[i], &T)); + fprintf(stream, "%f\n", T.SE); + } + fprintf(stream, "SCALARS failures_count unsigned_long_long 1\n"); + fprintf(stream, "LOOKUP_TABLE default\n"); + for(i = 0; i < szp; ++i) { + size_t nfails; + SDIS(estimator_get_failure_count(est[i], &nfails)); + ASSERT(nfails <= UINT_MAX); + fprintf(stream, "%u\n", (unsigned)nfails); + } + fprintf(stream, "SCALARS computation_time_estimate float 1\n"); + fprintf(stream, "LOOKUP_TABLE default\n"); + for(i = 0; i < szp; ++i) { + struct sdis_mc time; + SDIS(estimator_get_realisation_time(est[i], &time)); + fprintf(stream, "%f\n", time.E); + } + fprintf(stream, "SCALARS computation_time_std_dev float 1\n"); + fprintf(stream, "LOOKUP_TABLE default\n"); + for(i = 0; i < szp; ++i) { + struct sdis_mc time; + SDIS(estimator_get_realisation_time(est[i], &time)); + fprintf(stream, "%f\n", time.SE); + } +} + +res_T +dump_compute_region_at_the_end_of_vtk + (struct stardis* stardis, + FILE* stream) +{ + res_T res = RES_OK; + unsigned char* v = NULL; + unsigned tsz, i; + size_t j, psz; + ASSERT(stardis && stream); + psz = darray_size_t_size_get(&stardis->compute_surface.primitives); + ASSERT(psz == darray_sides_size_get(&stardis->compute_surface.sides)); + + ERR(sg3d_geometry_get_unique_triangles_count(stardis->geometry.sg3d, &tsz)); + /* For triangles not in compute region v==0 */ + v = MEM_CALLOC(stardis->allocator, tsz, sizeof(*v)); + if(!v) { + res = RES_MEM_ERR; + goto error; + } + + if(stardis->mode & SURFACE_COMPUTE_MODES) { + /* For triangles in compute surface, v==1 if FRONT or v==2 for BACK */ + FOR_EACH(j, 0, psz) { + size_t prim + = darray_size_t_cdata_get(&stardis->compute_surface.primitives)[j]; + enum sdis_side side + = darray_sides_cdata_get(&stardis->compute_surface.sides)[j]; + ASSERT(prim <= tsz); + v[(unsigned)prim] = + (unsigned char)(v[(unsigned)prim] | (side == SDIS_FRONT ? 1 : 2)); + } + + /* For triangles in compute surface with error v==MAX */ + FOR_EACH(j, 0, darray_uint_size_get(&stardis->compute_surface.err_triangles)) + { + unsigned prim + = darray_uint_cdata_get(&stardis->compute_surface.err_triangles)[j]; + ASSERT(prim <= tsz); + v[(unsigned)prim] = UCHAR_MAX; + } + } else { + unsigned descr[SG3D_PROP_TYPES_COUNT__]; + struct sdis_medium* medium; + const struct description* descriptions; + unsigned medium_id; + ASSERT(stardis->mode & MODE_MEDIUM_COMPUTE); + medium = find_medium_by_name(stardis, &stardis->solve_name, &medium_id); + ASSERT(medium != NULL); (void)medium; + descriptions = darray_descriptions_cdata_get(&stardis->descriptions); + FOR_EACH(i, 0, tsz) { + unsigned f_mid, b_mid; + /* Get the description IDs for this triangle */ + ERR(sg3d_geometry_get_unique_triangle_properties(stardis->geometry.sg3d, + i, descr)); + /* For triangles in compute volume, + * v==1 if FRONT, v==2 for BACK */ + if(descr[SG3D_FRONT] == SG3D_UNSPECIFIED_PROPERTY) + f_mid = UINT_MAX; + else description_get_medium_id(descriptions + descr[SG3D_FRONT], &f_mid); + if(descr[SG3D_BACK] == SG3D_UNSPECIFIED_PROPERTY) + b_mid = UINT_MAX; + else description_get_medium_id(descriptions + descr[SG3D_BACK], &b_mid); + if(f_mid == medium_id && b_mid == medium_id) + ; /* Keep v==0, not really a boundary */ + else if(f_mid == medium_id) + v[i] = 1; + else if(b_mid == medium_id) + v[i] = 2; + } + } + + fprintf(stream, "SCALARS Compute_region unsigned_int 1\n"); + fprintf(stream, "LOOKUP_TABLE default\n"); + FOR_EACH(i, 0, tsz) + fprintf(stream, "%u\n", v[i] == UCHAR_MAX ? UINT_MAX : v[i]); + +exit: + MEM_RM(stardis->allocator, v); + return res; +error: + goto exit; +} + +res_T +dump_model_as_c_chunks + (struct stardis* stardis, + FILE* stream) +{ + res_T res = RES_OK; + const char* prefix; + unsigned n, vcount, tcount; + + ASSERT(stardis && stream); + + prefix = str_cget(&stardis->chunks_prefix); + ERR(sg3d_geometry_get_unique_vertices_count(stardis->geometry.sg3d, &vcount)); + ERR(sg3d_geometry_get_unique_triangles_count(stardis->geometry.sg3d, &tcount)); + + fprintf(stream, "#define %s_UNSPECIFIED_PROPERTY %u\n\n", + prefix, SG3D_UNSPECIFIED_PROPERTY); + + fprintf(stream, "static const unsigned\n"); + fprintf(stream, "%s_vertices_count = %u;\n\n", prefix, vcount); + + fprintf(stream, "static const unsigned\n"); + fprintf(stream, "%s_triangles_count = %u;\n\n", prefix, tcount); + + fprintf(stream, "static const double\n"); + fprintf(stream, "%s_vertices[%u][3] = {\n", prefix, vcount); + for(n = 0; n < vcount; n++) { + double vertex[3]; + ERR(sg3d_geometry_get_unique_vertex(stardis->geometry.sg3d, n, + vertex)); + fprintf(stream, " { %g, %g, %g }%c\n", + SPLIT3(vertex), (n == vcount - 1 ? ' ' : ',')); + } + fprintf(stream, "};\n\n"); + + fprintf(stream, "static const unsigned\n"); + fprintf(stream, "%s_triangles[%u][3] = {\n", prefix, tcount); + for(n = 0; n < tcount; n++) { + unsigned triangle[3]; + ERR(sg3d_geometry_get_unique_triangle_vertices(stardis->geometry.sg3d, n, + triangle)); + fprintf(stream, " { %u, %u, %u }%c\n", + SPLIT3(triangle), (n == tcount - 1 ? ' ' : ',')); + } + fprintf(stream, "};\n\n"); + + fprintf(stream, "static const unsigned\n"); + fprintf(stream, "%s_properties[%u][3] = {\n", prefix, tcount); + for(n = 0; n < tcount; n++) { + unsigned properties[SG3D_PROP_TYPES_COUNT__]; + ERR(sg3d_geometry_get_unique_triangle_properties(stardis->geometry.sg3d, n, + properties)); + if(properties[0] == SG3D_UNSPECIFIED_PROPERTY) + fprintf(stream, " { %s_UNSPECIFIED_PROPERTY, ", prefix); + else fprintf(stream, " { %u, ", properties[0]); + if(properties[1] == SG3D_UNSPECIFIED_PROPERTY) + fprintf(stream, "%s_UNSPECIFIED_PROPERTY, ", prefix); + else fprintf(stream, "%u, ", properties[1]); + if(properties[2] == SG3D_UNSPECIFIED_PROPERTY) + fprintf(stream, "%s_UNSPECIFIED_PROPERTY }", prefix); + else fprintf(stream, "%u }", properties[2]); + if(n == tcount - 1) fprintf(stream, "\n"); else fprintf(stream, ",\n"); + } + fprintf(stream, "};\n\n"); + +exit: + return res; +error: + goto exit; +} diff --git a/src/stardis-output.h b/src/stardis-output.h @@ -0,0 +1,110 @@ +/* Copyright (C) 2018-2020 |Meso|Star> (contact@meso-star.com) + * + * This program 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. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef SDIS_OUTPUT_H +#define SDIS_OUTPUT_H + +#include <rsys/rsys.h> +#include <stdio.h> + +struct sdis_green_path; +struct sdis_heat_path; +struct sdis_estimator_buffer; +struct sdis_green_function; +struct counts; +struct description; +struct mem_allocator; +struct sdis_estimator; +struct stardis; +struct geometry; +struct vertex; +struct darray_estimators; + +struct dump_path_context { + unsigned long rank; + struct stardis* stardis; +}; + +extern res_T +dump_path + (const struct sdis_heat_path* path, + void* context); + +extern res_T +print_sample + (struct sdis_green_path* path, + void* ctx); + +extern res_T +dump_vtk_image + (const struct sdis_estimator_buffer* buf, + FILE* stream); + +extern res_T +dump_ht_image + (const struct sdis_estimator_buffer* buf, + FILE* stream); + +extern res_T +dump_green_ascii + (struct sdis_green_function* green, + const struct stardis* stardis, + FILE* stream); + +extern res_T +dump_green_bin + (struct sdis_green_function* green, + const struct stardis* stardis, + FILE* stream); + +extern res_T +dump_paths_end + (struct sdis_green_function* green, + const struct stardis* stardis, + FILE* stream); + +extern res_T +dump_enclosure_related_stuff_at_the_end_of_vtk + (struct stardis* stardis, + FILE* stream); + +extern res_T +print_single_MC_result + (struct sdis_estimator* estimator, + struct stardis* stardis, + FILE* stream); + +extern void +dump_map + (const struct stardis* stardis, + const struct darray_estimators* estimators, + FILE* stream); + +extern res_T +dump_boundaries_at_the_end_of_vtk + (const struct stardis* stardis, + FILE* stream); + +extern res_T +dump_compute_region_at_the_end_of_vtk + (struct stardis* stardis, + FILE* stream); + +extern res_T +dump_model_as_c_chunks + (struct stardis* stardis, + FILE* stream); + +#endif diff --git a/src/stardis-parsing.c b/src/stardis-parsing.c @@ -0,0 +1,1820 @@ +/* Copyright (C) 2018-2020 |Meso|Star> (contact@meso-star.com) + * + * This program 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. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +#define _POSIX_C_SOURCE 200809L /* strdup */ +#include "stardis-parsing.h" +#include "stardis-app.h" +#include "stardis-default.h" +#include "stardis-version.h" + +#include <rsys/cstr.h> +#include <rsys/double2.h> +#include <rsys/double3.h> +#include <sdis_version.h> +#include <rsys/logger.h> + +#include <getopt.h> +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> + +/******************************************************************************* + * Local Functions + ******************************************************************************/ + +#ifdef COMPILER_GCC +static char* +_strupr + (char* s) +{ + char* ptr; + for(ptr = s; *ptr; ++ptr) { + int tmp = toupper(*ptr); + ASSERT(tmp == (char)tmp); + *ptr = (char)tmp; + } + return s; +} +#endif + +static char** +split_line + (char* a_str, + const char a_delim) +{ + char** result = 0; + size_t chunks_count; + char* tmp = a_str; + char delim[2]; + char* tok_ctx = NULL; + + ASSERT(a_str); + + delim[0] = a_delim; + delim[1] = 0; + + /* if a_str starts with initial useless delimiters remove them */ + while(*a_str == a_delim) a_str++; + + /* if a_str ends with final useless delimiters remove them */ + tmp = a_str + strlen(a_str) - 1; + while(*tmp == a_delim && tmp >= a_str) { *tmp = '\0'; tmp--; } + + if(tmp >= a_str) chunks_count = 1; + else return NULL; + + tmp = a_str; + while(*tmp) { + int delim_found = 0; + while(*tmp == a_delim) { delim_found = 1; tmp++; } + if(delim_found) chunks_count++; + tmp++; + } + + /* Add space for terminating null string so caller + knows where the list of returned strings ends. */ + result = malloc(sizeof(char*) * (1 + chunks_count)); + if(result) { + size_t idx = 0; + char* token = strtok_r(a_str, delim, &tok_ctx); + + while(token) { + ASSERT(idx <= chunks_count); +#ifdef COMPILER_CL + *(result + idx++) = _strdup(token); +#else + *(result + idx++) = strdup(token); +#endif + token = strtok_r(NULL, delim, &tok_ctx); + } + ASSERT(idx == chunks_count); + *(result + idx) = 0; + } + return result; +} + +void +print_version + (FILE* stream) +{ + ASSERT(stream); + fprintf(stream, + "Stardis version %i.%i.%i built on stardis solver version %i.%i.%i\n", + STARDIS_APP_VERSION_MAJOR, STARDIS_APP_VERSION_MINOR, STARDIS_APP_VERSION_PATCH, + Stardis_VERSION_MAJOR, Stardis_VERSION_MINOR, Stardis_VERSION_PATCH); +} + +void +add_geom_ctx_indices + (const unsigned itri, + unsigned ids[3], + void* context) +{ + const struct add_geom_ctx* ctx = context; + const unsigned* trg; + int i; + ASSERT(ids && ctx); + ASSERT(itri < ctx->stl_desc.triangles_count); + trg = ctx->stl_desc.indices + 3 * itri; + for(i = 0; i < 3; i++) ids[i] = trg[i]; +} + +void +add_geom_ctx_properties + (const unsigned itri, + unsigned prop[3], + void* context) +{ + const struct add_geom_ctx* ctx = context; + int i; + ASSERT(prop && ctx); (void)itri; + ASSERT(itri < ctx->stl_desc.triangles_count); + /* Same media for the whole add_geometry set of triangles */ + for(i = 0; i < SG3D_PROP_TYPES_COUNT__; i++) prop[i] = ctx->properties[i]; +} + +void +add_geom_ctx_position + (const unsigned ivert, + double pos[3], + void* context) +{ + const struct add_geom_ctx* ctx = context; + const float* v; + ASSERT(pos && ctx); + ASSERT(ivert < ctx->stl_desc.vertices_count); + v = ctx->stl_desc.vertices + 3 * ivert; + d3_set_f3(pos, v); +} + +static res_T +add_geom_keep_degenerated + (const unsigned itri, + void* context, + int* abort) +{ + const struct add_geom_ctx* ctx = context; + struct darray_uint* degenerated; + + ASSERT(abort && ctx && ctx->custom); (void)abort; + ASSERT(itri < ctx->stl_desc.triangles_count); + degenerated = ctx->custom; + return darray_uint_push_back(degenerated, &itri); +} + +static res_T +read_sides_and_files + (struct stardis* stardis, + const int descr_is_intface, /* if 1, don't read side */ + const unsigned description_id, + char** tok_ctx) +{ + char* tk = NULL; + int file_count = 0; + struct sstl* sstl = NULL; + struct add_geom_ctx add_geom_ctx; + unsigned current_merge_errors; + struct sg3d_geometry_add_callbacks callbacks = SG3D_ADD_CALLBACKS_NULL__; + struct darray_uint degenerated; + struct str str; + res_T res = RES_OK; + + ASSERT(stardis && tok_ctx); + + darray_uint_init(stardis->allocator, &degenerated); + str_init(stardis->allocator, &str); + callbacks.get_indices = add_geom_ctx_indices; + callbacks.get_properties = add_geom_ctx_properties; + callbacks.get_position = add_geom_ctx_position; + callbacks.degenerated_triangle = add_geom_keep_degenerated; + add_geom_ctx.custom = &degenerated; + + ERR(sg3d_geometry_get_unique_triangles_with_merge_conflict_count( + stardis->geometry.sg3d, &current_merge_errors)); + + /* At least one side+name, no side without name */ + ERR(sstl_create(stardis->logger, stardis->allocator, 1, &sstl)); + for(;;) { + unsigned merge_errors; + if(descr_is_intface) { + add_geom_ctx.properties[SG3D_FRONT] = SG3D_UNSPECIFIED_PROPERTY; + add_geom_ctx.properties[SG3D_BACK] = SG3D_UNSPECIFIED_PROPERTY; + add_geom_ctx.properties[SG3D_INTFACE] = description_id; + } else { + tk = strtok_r(NULL, " \t", tok_ctx); + if(!tk) { + if(file_count == 0) { + /* At least 1 side */ + logger_print(stardis->logger, LOG_ERROR, + "Invalid data (missing token 'side')\n"); + res = RES_BAD_ARG; + goto error; + } + else break; + } + _strupr(tk); + add_geom_ctx.properties[SG3D_INTFACE] = SG3D_UNSPECIFIED_PROPERTY; + if(0 == strcmp(tk, "FRONT")) { + add_geom_ctx.properties[SG3D_FRONT] = description_id; + add_geom_ctx.properties[SG3D_BACK] = SG3D_UNSPECIFIED_PROPERTY; + } + else if(0 == strcmp(tk, "BACK")) { + add_geom_ctx.properties[SG3D_FRONT] = SG3D_UNSPECIFIED_PROPERTY; + add_geom_ctx.properties[SG3D_BACK] = description_id; + } + else if(0 == strcmp(tk, "BOTH")) { + add_geom_ctx.properties[SG3D_FRONT] = description_id; + add_geom_ctx.properties[SG3D_BACK] = description_id; + } + else { + logger_print(stardis->logger, LOG_ERROR, + "Invalid side specifier: %s\n", tk); + res = RES_BAD_ARG; + goto error; + } + } + tk = strtok_r(NULL, " \t", tok_ctx); + if(!tk) { + if(!descr_is_intface /* Has read a side */ + || !file_count) /* Need at least 1 file */ + { + logger_print(stardis->logger, LOG_ERROR, + "Invalid data (missing token 'file name')\n"); + res = RES_BAD_ARG; + goto error; + } + else break; + } + file_count++; + res = sstl_load(sstl, tk); + if(res != RES_OK) { + logger_print(stardis->logger, LOG_ERROR, + "Cannot read STL file: '%s'\n", tk); + goto error; + } + ERR(sstl_get_desc(sstl, &add_geom_ctx.stl_desc)); + ASSERT(add_geom_ctx.stl_desc.vertices_count <= UINT_MAX + && add_geom_ctx.stl_desc.triangles_count <= UINT_MAX); + + res = sg3d_geometry_add( + stardis->geometry.sg3d, + (unsigned)add_geom_ctx.stl_desc.vertices_count, + (unsigned)add_geom_ctx.stl_desc.triangles_count, + &callbacks, + &add_geom_ctx); + if(darray_uint_size_get(&degenerated)) { + size_t c, n; + const unsigned* ids = darray_uint_cdata_get(&degenerated); + c = darray_uint_size_get(&degenerated); + ASSERT(c <= ULONG_MAX); + logger_print(stardis->logger, LOG_WARNING, + "File '%s' included %lu degenerated triangles (removed)\n", + tk, (unsigned long)c); + ERR(str_printf(&str, "Degenerated triangles IDs: %u", ids[0])); + FOR_EACH(n, 1, c) { STR_APPEND_PRINTF(&str, ", %u", ARG1( ids[n] ) ); } + logger_print(stardis->logger, LOG_OUTPUT, "%s\n", str_cget(&str)); + darray_uint_clear(&degenerated); + } + + if(res != RES_OK) { + logger_print(stardis->logger, LOG_ERROR, + "Cannot add file content: '%s'\n", tk); + goto error; + } + /* Check conflicts */ + ERR(sg3d_geometry_get_unique_triangles_with_merge_conflict_count( + stardis->geometry.sg3d, &merge_errors)); + if(current_merge_errors != merge_errors) { + int is_for_compute = + (stardis->mode & COMPUTE_MODES) && !(stardis->mode & MODE_DUMP_VTK); + logger_print(stardis->logger, (is_for_compute ? LOG_ERROR : LOG_WARNING), + "Merge conflicts found reading file '%s' (%u triangles).\n", + tk, merge_errors - current_merge_errors); + if(is_for_compute) { + res = RES_BAD_ARG; + goto error; + } + } + current_merge_errors = merge_errors; + } + +end: + darray_uint_release(&degenerated); + str_release(&str); + if(sstl) SSTL(ref_put(sstl)); + return res; +error: + goto end; +} + +/******************************************************************************* + * Public Functions + ******************************************************************************/ + +res_T +init_args + (struct logger* logger, + struct mem_allocator* allocator, + struct args** out_args) +{ + res_T res = RES_OK; + struct args* args = NULL; + ASSERT(logger && allocator && out_args); + + args = calloc(sizeof(struct args), 1); + if(!args) { + res = RES_MEM_ERR; + goto error; + } + + args->logger = logger; + args->allocator = allocator; + darray_str_init(allocator, &args->model_files); + /* Set default values */ + args->samples = STARDIS_DEFAULT_SAMPLES_COUNT; + args->nthreads = SDIS_NTHREADS_DEFAULT; + d2(args->pos_and_time+3, STARDIS_DEFAULT_COMPUTE_TIME); + args->ambient_temp = STARDIS_DEFAULT_AMBIENT_TEMP; + args->ref_temp = STARDIS_DEFAULT_REFERENCE_TEMP; + args->verbose = STARDIS_DEFAULT_VERBOSE_LEVEL; + +end: + *out_args = args; + return res; +error: + if(args) release_args(args); + args = NULL; + goto end; +} + +void +release_args(struct args* args) +{ + ASSERT(args); + darray_str_release(&args->model_files); + free(args); +} + +char +mode_option + (const int m) +{ + int found = 0; + char res = '?'; + if(m & MODE_DUMP_C_CHUNKS) { found++; res = 'c'; } + if(m & MODE_DUMP_VTK) { found++; res = 'd'; } + if(m & MODE_DUMP_PATHS) { found++; res = 'D'; } + if(m & MODE_EXTENDED_RESULTS) { found++; res = 'e'; } + if(m & MODE_FLUX_BOUNDARY_COMPUTE) { found++; res = 'F'; } + if(m & MODE_GREEN) { found++; res = 'g'; } + if(m & MODE_BIN_GREEN) { found++; res = 'G'; } + if(m & MODE_DUMP_HELP) { found++; res = 'h'; } + if(m & MODE_MEDIUM_COMPUTE) { found++; res = 'm'; } + if(m & MODE_PROBE_COMPUTE) { found++; res = 'p'; } + if(m & MODE_PROBE_COMPUTE_ON_INTERFACE) { found++; res = 'P'; } + if(m & MODE_IR_COMPUTE) { found++; res = 'R'; } + if(m & MODE_BOUNDARY_COMPUTE) { found++; res = 's'; } + if(m & MODE_MAP_COMPUTE) { found++; res = 'S'; } + if(m & MODE_VERBOSITY) { found++; res = 'V'; } + if(m & MODE_DUMP_VERSION) { found++; res = 'v'; } + ASSERT(found == 1); + return res; +} + +void +print_multiple_modes + (char* buf, + const size_t sz, + const int modes, + const int dont) /* Modes in dont are not printed */ +{ + int b = 0, fst = 1; + int m = UNDEF_MODE; + size_t left = sz; + ASSERT(buf); + do { + m = BIT(b++); + if(m & dont) continue; + if(m & modes) { + size_t n = + (size_t)snprintf(buf, left, (fst ? "-%c" : ", -%c"), mode_option(m)); + if(n >= left) FATAL("Buffer is too small."); + left -= n; + buf += n; + fst = 0; + } + } while(m < modes); +} + +void +short_help + (FILE* stream, + const char* prog) +{ + const char* name; + ASSERT(stream && prog); + +#ifdef COMPILER_GCC + name = strrchr(prog, '/'); +#else + name = strrchr(prog, '\\'); +#endif + + name = name ? name + 1 : prog; + fprintf(stream, + "Usage: %s [OPTIONS]\n" + "\nSolve coupled thermal systems under the linear assumption.\n" + "Refer to stardis(1) man page for more information.\n\n", + name); + print_version(stream); + + fprintf(stream, "\nMandatory options\n"); + fprintf(stream, "-------------------\n"); + + fprintf(stream, "\n -M <FILE>\n"); + fprintf(stream, " Read a text file that contains (partial) description of the model.\n"); + + fprintf(stream, "\nExclusive options\n"); + fprintf(stream, "-------------------\n"); + + fprintf(stream, "\n -F STL_FILE[,TIME-RANGE]\n"); + fprintf(stream, " Compute the mean flux on a given 2D region at a given time.\n"); + + fprintf(stream, "\n -m MEDIUM_NAME[,TIME-RANGE]\n"); + fprintf(stream, " Compute the mean temperature in a given medium at a given time.\n"); + + fprintf(stream, "\n -p X,Y,Z[,TIME-RANGE]\n"); + fprintf(stream, " Compute the temperature at the given probe.\n"); + + fprintf(stream, "\n -P X,Y,Z[,TIME-RANGE]\n"); + fprintf(stream, " Compute the temperature at the given probe on an interface.\n"); + + fprintf(stream, "\n -R [RENDERING_OPTIONS]\n"); + fprintf(stream, " Compute an infra-red image of the model.\n"); + + fprintf(stream, "\n -s STL_FILE[,TIME-RANGE]\n"); + fprintf(stream, " Compute the mean temperature on a given 2D region.\n"); + + fprintf(stream, "\n -S STL_FILE[,TIME-RANGE]\n"); + fprintf(stream, " Compute the by-triangle mean temperature on a given 2D region.\n"); + + fprintf(stream, "\nOther options\n"); + fprintf(stream, "-------------------\n"); + + fprintf(stream, "\n -a AMBIENT_TEMP\n"); + fprintf(stream, " Set the ambient radiative temperature.\n"); + + fprintf(stream, "\n -c NAMES_PREFIX\n"); + fprintf(stream, " Dump the geometry and property ids to stdout as C chunks.\n"); + + fprintf(stream, "\n -d\n"); + fprintf(stream, " Dump the geometry to stdout in VTK format along with various properties.\n"); + + fprintf(stream, "\n -D TYPE,FILE_NAMES_PREFIX\n"); + fprintf(stream, " Write thermal paths of the given TYPE in VTK format.\n"); + + fprintf(stream, "\n -e\n"); + fprintf(stream, " Use extended format to output Monte-Carlo results.\n"); + + fprintf(stream, "\n -g\n"); + fprintf(stream, " Change the computation to produce the green function.\n"); + + fprintf(stream, "\n -G BIN_FILE_NAME[,CSV_FILE_NAME]\n"); + fprintf(stream, " Change the computation to produce the green function and possibly end of paths information.\n"); + + fprintf(stream, "\n -h\n"); + fprintf(stream, " Print this help and exit.\n"); + + fprintf(stream, "\n -n SAMPLE_COUNT\n"); + fprintf(stream, " Set the number of Monte-Carlo samples.\n"); + + fprintf(stream, "\n -r REFERENCE_TEMP\n"); + fprintf(stream, " Set the temperature used for the linearization of the radiative transfer.\n"); + + fprintf(stream, "\n -t NUM_OF_THREADS\n"); + fprintf(stream, " Hint on the number of threads.\n"); + + fprintf(stream, "\n -v\n"); + fprintf(stream, " Print version information and exit.\n"); + + fprintf(stream, "\n -V LEVEL\n"); + fprintf(stream, " Set the verbosity level.\n"); +} + +/* Workaround for a gcc warning when GET_OPTIONAL_TIME_RANGE used with Rank=0 */ +FINLINE int is_less(size_t a, size_t b) { return a < b; } + +/* Get a time range from a coma-separated list of doubles + * The first Rank values are mandatory, followed by an optional time range + * that can be a single time */ +#define GET_OPTIONAL_TIME_RANGE(Src, Rank, Dst, Logger, OptionString, Option) \ + res = cstr_to_list_double((Src), ',', (Dst), &len, (Rank)+2); \ + if(res != RES_OK \ + || is_less(len, (Rank)) \ + || (len == (Rank)+1 && (Dst)[(Rank)] < 0) \ + || (len == (Rank)+2 && ((Dst)[0] < 0 || (Dst)[(Rank)] > (Dst)[(Rank)+1])) \ + || len > (Rank)+2) \ + { \ + if(res == RES_OK) res = RES_BAD_ARG; \ + logger_print((Logger), LOG_ERROR, \ + "Invalid argument for option "OptionString": %s\n", \ + (Option), (Src)); \ + goto error; \ + } else { \ + if(len == (Rank)+1) (Dst)[(Rank)+1] = (Dst)[(Rank)];\ + } + + /* Get a string followed by an optional time range */ +#define GET_STR_AND_OPTIONAL_TIME_RANGE(Str, Time) \ + ptr = strchr(optarg, ','); /* First ',' */ \ + if(ptr) { /* Time range provided */ \ + GET_OPTIONAL_TIME_RANGE(ptr+1, 0, (Time), args->logger, "-%c", opt); \ + *ptr = '\0'; \ + } \ + (Str) = optarg; + +/* Get a position followed by an optional time range */ +#define GET_POS_AND_OPTIONAL_TIME_RANGE(Dst) \ + GET_OPTIONAL_TIME_RANGE(optarg, 3, (Dst), args->logger, "-%c", opt); + +res_T +parse_args + (const int argc, + char** argv, + struct args* args) +{ + int opt = 0, n_used = 0; + size_t len = 0; + const char option_list[] = "a:c:dD:eF:gG:hm:M:n:p:P:r:R:s:S:t:vV:"; + char buf[128]; + res_T res = RES_OK; + + ASSERT(argv && args); + + opterr = 0; /* No default error messages */ + while((opt = getopt(argc, argv, option_list)) != -1) { + switch (opt) { + + case '?': /* Unreconised option */ + { + char* ptr = strchr(option_list, optopt); + res = RES_BAD_ARG; + if(ptr && ptr[1] == ':') { + logger_print(args->logger, LOG_ERROR, + "Missing argument for option -%c\n", + optopt); + } else { + logger_print(args->logger, LOG_ERROR, "Invalid option -%c\n", optopt); + } + goto error; + } + + case 'a': + res = cstr_to_double(optarg, &args->ambient_temp); + if(res != RES_OK + || args->ambient_temp < 0) + { + if(res == RES_OK) res = RES_BAD_ARG; + logger_print(args->logger, LOG_ERROR, + "Invalid argument for option -%c: %s\n", + opt, optarg); + goto error; + } + break; + + case 'c': + if(args->mode & USE_STDOUT_MODES) { + res = RES_BAD_ARG; + print_multiple_modes(buf, sizeof(buf), USE_STDOUT_MODES, MODE_DUMP_C_CHUNKS); + logger_print(args->logger, LOG_ERROR, + "Option -%c cannot be used in conjunction with other dump options (%s).\n", + (char)opt, buf); + goto error; + } + args->chunks_prefix = optarg; + args->mode |= MODE_DUMP_C_CHUNKS; + break; + + case 'd': + if(args->mode & USE_STDOUT_MODES) { + res = RES_BAD_ARG; + print_multiple_modes(buf, sizeof(buf), USE_STDOUT_MODES, MODE_DUMP_VTK); + logger_print(args->logger, LOG_ERROR, + "Option -%c cannot be used in conjunction with other dump options (%s).\n", + (char)opt, buf); + goto error; + } + args->mode |= MODE_DUMP_VTK; + break; + + case 'D': { + char* ptr = strrchr(optarg, ','); + if(!ptr || ptr != strchr(optarg, ',')) + res = RES_BAD_ARG; /* Single ',' expected */ + else { + args->paths_filename = ptr + 1; + *ptr = '\0'; + } + if(res == RES_OK) { + if(0 == strcmp(optarg, "all")) { + args->dump_paths = DUMP_ALL; + } + else if(0 == strcmp(optarg, "error")) { + args->dump_paths = DUMP_ERROR; + } + else if(0 == strcmp(optarg, "success")) { + args->dump_paths = DUMP_SUCCESS; + } + } + if(res != RES_OK) { + res = RES_BAD_ARG; + logger_print(args->logger, LOG_ERROR, + "Invalid argument for option -%c: %s\n", + opt, optarg); + goto error; + } + args->mode |= MODE_DUMP_PATHS; + break; + } + + case 'e': + args->mode |= MODE_EXTENDED_RESULTS; + break; + + /*case 'F': see 's' */ + + case 'g': + if(args->mode & MODE_BIN_GREEN) { + res = RES_BAD_ARG; + logger_print(args->logger, LOG_ERROR, + "Options -%c and -%c are exclusive.\n", + (char)opt, mode_option(MODE_BIN_GREEN)); + goto error; + } + args->mode |= MODE_GREEN; + break; + + case 'G': { + char* ptr = strrchr(optarg, ','); + if(ptr && ptr != strchr(optarg, ',')) + res = RES_BAD_ARG; /* Expecting 1 or 0 ',' */ + if(args->mode & (MODE_BIN_GREEN | MODE_GREEN)) { + res = RES_BAD_ARG; + if(args->mode & MODE_BIN_GREEN) + logger_print(args->logger, LOG_ERROR, + "Option -%c cannot be used twice.\n", + (char)opt); + else + logger_print(args->logger, LOG_ERROR, + "Options -%c and -%c are exclusive.\n", + (char)opt, mode_option(MODE_GREEN)); + goto error; + } + args->mode |= MODE_BIN_GREEN; + if(ptr) { + args->end_paths_filename = ptr + 1; + *ptr = '\0'; + } + args->bin_green_filename = optarg; + break; + } + + case 'h': + if(args->mode & USE_STDOUT_MODES) { + res = RES_BAD_ARG; + print_multiple_modes(buf, sizeof(buf), USE_STDOUT_MODES, MODE_DUMP_HELP); + logger_print(args->logger, LOG_ERROR, + "Option -%c cannot be used in conjunction with other dump options (%s).\n", + (char)opt, buf); + goto error; + } + args->mode |= MODE_DUMP_HELP; + break; + + case 'm': { + char* ptr; + if(args->mode & EXCLUSIVE_MODES) { + res = RES_BAD_ARG; + logger_print(args->logger, LOG_ERROR, + "Options -%c and -%c are exclusive.\n", + (char)opt, mode_option(args->mode)); + goto error; + } + args->mode |= MODE_MEDIUM_COMPUTE; + GET_STR_AND_OPTIONAL_TIME_RANGE(args->medium_name, args->pos_and_time + 3); + break; + } + + case 'M': { + struct str name; + str_init(args->allocator, &name); + ERR(str_set(&name, optarg)); + ERR(darray_str_push_back(&args->model_files, &name)); + str_release(&name); + break; + } + case 'n': { + unsigned long n; + res = cstr_to_ulong(optarg, &n); + if(res != RES_OK + || n == 0) + { + if(res == RES_OK) res = RES_BAD_ARG; + logger_print(args->logger, LOG_ERROR, + "Invalid argument for option -%c: %s\n", + opt, optarg); + goto error; + } + args->samples = n; + n_used = 1; + break; + } + + case 'p': + if(args->mode & EXCLUSIVE_MODES) { + res = RES_BAD_ARG; + logger_print(args->logger, LOG_ERROR, + "Options -%c and -%c are exclusive.\n", + (char)opt, mode_option(args->mode)); + goto error; + } + args->mode |= MODE_PROBE_COMPUTE; + GET_POS_AND_OPTIONAL_TIME_RANGE(args->pos_and_time); + break; + + case 'P': + if(args->mode & EXCLUSIVE_MODES) { + res = RES_BAD_ARG; + logger_print(args->logger, LOG_ERROR, + "Options -%c and -%c are exclusive.\n", + (char)opt, mode_option(args->mode)); + goto error; + } + args->mode |= MODE_PROBE_COMPUTE_ON_INTERFACE; + GET_POS_AND_OPTIONAL_TIME_RANGE(args->pos_and_time); + break; + + case 'r': + res = cstr_to_double(optarg, &args->ref_temp); + if(res != RES_OK + || args->ref_temp < 0) + { + if(res == RES_OK) res = RES_BAD_ARG; + logger_print(args->logger, LOG_ERROR, + "Invalid argument for option -%c: %s\n", + opt, optarg); + goto error; + } + break; + + case 'R': + if(args->mode & EXCLUSIVE_MODES) { + res = RES_BAD_ARG; + logger_print(args->logger, LOG_ERROR, + "Options -%c and -%c are exclusive.\n", + (char)opt, mode_option(args->mode)); + goto error; + } + args->mode |= MODE_IR_COMPUTE; + args->camera = optarg; + break; + + case 's': + case 'S': + case 'F': { + char *ptr; + if(args->mode & EXCLUSIVE_MODES) { + res = RES_BAD_ARG; + logger_print(args->logger, LOG_ERROR, + "Options -%c and -%c are exclusive.\n", + (char)opt, mode_option(args->mode)); + goto error; + } + switch (opt) { + case 's': + args->mode |= MODE_BOUNDARY_COMPUTE; + break; + case 'S': + args->mode |= MODE_MAP_COMPUTE; + break; + case 'F': + args->mode |= MODE_FLUX_BOUNDARY_COMPUTE; + break; + } + GET_STR_AND_OPTIONAL_TIME_RANGE(args->solve_filename, args->pos_and_time + 3); + break; + } + + case 't': + res = cstr_to_uint(optarg, &args->nthreads); + if(res != RES_OK + || args->nthreads <= 0) + { + if(res == RES_OK) res = RES_BAD_ARG; + logger_print(args->logger, LOG_ERROR, + "Invalid argument for option -%c: %s\n", + opt, optarg); + goto error; + } + break; + + case 'v': + if(args->mode & USE_STDOUT_MODES) { + res = RES_BAD_ARG; + print_multiple_modes(buf, sizeof(buf), USE_STDOUT_MODES, MODE_DUMP_VERSION); + logger_print(args->logger, LOG_ERROR, + "Option -%c cannot be used in conjunction with other dump options (%s).\n", + (char)opt, buf); + goto error; + } + args->mode |= MODE_DUMP_VERSION; + break; + + case 'V': + res = cstr_to_int(optarg, &args->verbose); + if(res != RES_OK + || args->verbose < 0 + || args->verbose > 3) + { + if(res == RES_OK) res = RES_BAD_ARG; + logger_print(args->logger, LOG_ERROR, + "Invalid argument for option -%c: %s\n", + opt, optarg); + goto error; + } + break; + } + } + + if(argc > optind) { + int i; + for(i = optind; i < argc; i++) { + logger_print(args->logger, LOG_ERROR, "Unexpected argument: %s.\n", argv[i]); + } + res = RES_BAD_ARG; + goto error; + } + + if(!darray_str_size_get(&args->model_files) + && !(args->mode & SHORT_EXIT_MODES)) { + logger_print(args->logger, LOG_ERROR, + "Missing mandatory argument: -M <model_file_name>\n"); + res = RES_BAD_ARG; + goto error; + } + + if(args->mode == UNDEF_MODE) { + print_multiple_modes(buf, sizeof(buf), EXCLUSIVE_MODES | USE_STDOUT_MODES, 0); + logger_print(args->logger, LOG_WARNING, + "Nothing to do.\nOne of the following options should be used: %s\n", + buf); + res = RES_BAD_ARG; + goto error; + } + + if(args->mode & (MODE_BIN_GREEN | MODE_GREEN) + && !(args->mode & GREEN_COMPATIBLE_MODES)) + { + print_multiple_modes(buf, sizeof(buf), GREEN_COMPATIBLE_MODES, 0); + logger_print(args->logger, LOG_ERROR, + "Option -%c can only be used in conjunction with: %s\n", + mode_option(args->mode & (MODE_BIN_GREEN | MODE_GREEN)), buf); + res = RES_BAD_ARG; + goto error; + } + + if(args->mode & MODE_IR_COMPUTE && n_used) { + logger_print(args->logger, LOG_ERROR, + "The -n option has no effect in rendering mode;" + " use rendering's SPP suboption instead.\n"); + res = RES_BAD_ARG; + goto error; + } + + if(args->mode & MODE_DUMP_PATHS) { + if(!(args->mode & COMPUTE_MODES)) { + res = RES_BAD_ARG; + print_multiple_modes(buf, sizeof(buf), COMPUTE_MODES, 0); + logger_print(args->logger, LOG_ERROR, + "Option -%c can only be used in conjunction with an option" + " that samples heat paths (%s).\n", + mode_option(MODE_DUMP_PATHS), buf); + goto error; + } + if(args->mode & (MODE_BIN_GREEN | MODE_GREEN)) { + res = RES_BAD_ARG; + logger_print(args->logger, LOG_ERROR, + "Option -%c cannot be used in conjunction with -%c nor -%c.\n", + mode_option(MODE_DUMP_PATHS), mode_option(MODE_GREEN) + , mode_option(MODE_BIN_GREEN)); + goto error; + } + } + + if(args->mode & MODE_EXTENDED_RESULTS) { + if(!(args->mode & EXT_COMPATIBLE_MODES)) { + res = RES_BAD_ARG; + print_multiple_modes(buf, sizeof(buf), EXT_COMPATIBLE_MODES, 0); + logger_print(args->logger, LOG_ERROR, + "Option -%c can only be used in conjunction with an option" + " that computes a single Monte-Carlo (%s).\n", + mode_option(MODE_EXTENDED_RESULTS), buf); + goto error; + } + if(args->mode & (MODE_BIN_GREEN | MODE_GREEN)) { + res = RES_BAD_ARG; + logger_print(args->logger, LOG_ERROR, + "Option -%c cannot be used in conjunction with -%c nor -%c.\n", + mode_option(MODE_EXTENDED_RESULTS), mode_option(MODE_GREEN) + , mode_option(MODE_BIN_GREEN)); + goto error; + } + } + +end: + return res; +error: + logger_print(args->logger, LOG_ERROR, "Use option -h to print help.\n"); + goto end; +} + +res_T +parse_camera + (struct logger* logger, + char* cam_param, + struct stardis* stardis) +{ + char** line = NULL; + char** opt = NULL; + struct camera* cam; + res_T res = RES_OK; + + ASSERT(cam_param && stardis); + cam = &stardis->camera; + line = split_line(cam_param, ':'); + + if(line) { + struct str keep; + int i = 0; + str_init(stardis->allocator, &keep); + for(i = 0; *(line + i); i++) { + size_t len = 0; + str_set(&keep, line[i]); + opt = split_line(line[i], '='); + if(!opt[0] || ! opt[1] || opt[2]) { + if(res == RES_OK) res = RES_BAD_ARG; + logger_print((logger), LOG_ERROR, + "Invalid option syntax: %s\n", str_cget(&keep)); + goto error; + } + str_set(&keep, opt[0]); + _strupr(opt[0]); + if(strcmp(opt[0], "T") == 0) { + GET_OPTIONAL_TIME_RANGE(opt[1], 0, cam->time_range, logger, "%s", + str_cget(&keep)); + } + else if(strcmp(opt[0], "FMT") == 0) { + _strupr(opt[1]); + if(strcmp(opt[1], "VTK") == 0) + cam->fmt = STARDIS_RENDERING_OUTPUT_FILE_FMT_VTK; + else if(strcmp(opt[1], "HT") == 0) + cam->fmt = STARDIS_RENDERING_OUTPUT_FILE_FMT_HT; + else { + logger_print(logger, LOG_ERROR, + "Unexpected value for rendering option %s: %s.\n", + opt[0], opt[1]); + res = RES_BAD_ARG; + goto error; + } + } + else if(strcmp(opt[0], "FOV") == 0) { + ERR(cstr_to_double(opt[1], &cam->fov)); + } + else if(strcmp(opt[0], "UP") == 0) { + ERR(cstr_to_list_double(opt[1], ',', cam->up, &len, 3)); + } + else if(strcmp(opt[0], "TGT") == 0) { + ERR(cstr_to_list_double(opt[1], ',', cam->tgt, &len, 3)); + cam->auto_look_at = 0; + } + else if(strcmp(opt[0], "POS") == 0) { + ERR(cstr_to_list_double(opt[1], ',', cam->pos, &len, 3)); + cam->auto_look_at = 0; + } + else if(strcmp(opt[0], "IMG") == 0) { + unsigned img_sz[2]; + ERR(cstr_to_list_uint(opt[1], 'x', img_sz, &len, 2)); + cam->img_width = img_sz[0]; + cam->img_height = img_sz[1]; + } + else if(strcmp(opt[0], "SPP") == 0) { + ERR(cstr_to_uint(opt[1], &cam->spp)); + } else { + logger_print(logger, LOG_ERROR, + "Unexpected option for rendering mode: %s.\n", + opt[0]); + res = RES_BAD_ARG; + goto error; + } + } + str_release(&keep); + } + +end: +#define FREE_AARRAY(ARRAY) if(ARRAY) {\ + int i = 0; \ + for(i=0; *(ARRAY+i);i++){\ + free(ARRAY[i]);\ + }\ + free(ARRAY);\ +} + FREE_AARRAY(line) + FREE_AARRAY(opt) +#undef FREE_AARRAY + + return res; +error: + logger_print(logger, LOG_ERROR, "Error parsing camera options.\n"); + logger_print(logger, LOG_ERROR, "Use the -h option to get help.\n"); + goto end; +} + +#undef GET_STR_AND_OPTIONAL_TIME_RANGE +#undef GET_POS_AND_OPTIONAL_TIME_RANGE +#undef GET_OPTIONAL_TIME_RANGE + +static struct description* +find_description_by_name + (struct stardis* stardis, + const struct str* name, + size_t* out_id) +{ + size_t i; + ASSERT(stardis && name); + + FOR_EACH(i, 0, darray_descriptions_size_get(&stardis->descriptions)) { + struct description* desc + = darray_descriptions_data_get(&stardis->descriptions) + i; + if(str_eq(name, get_description_name(desc))) { + if(out_id) *out_id = i; + return desc; + } + } + return NULL; +} + +/* H_BOUNDARY_FOR_SOLID Name emissivity specular_fraction hc T_env STL_filenames + * H_BOUNDARY_FOR_FLUID Name emissivity specular_fraction hc T_env STL_filenames */ +static res_T +process_h + (struct stardis* stardis, + struct dummies* dummies, + const enum description_type type, + char** tok_ctx) +{ + char* tk = NULL; + struct description* desc; + size_t sz; + res_T res = RES_OK; + + ASSERT(stardis && tok_ctx); + + stardis->counts.hbound_count++; + + sz = darray_descriptions_size_get(&stardis->descriptions); + ERR(darray_descriptions_resize(&stardis->descriptions, sz+1)); + desc = darray_descriptions_data_get(&stardis->descriptions) + sz; + init_h(stardis->allocator, &desc->d.h_boundary); + + desc->type = type; + + CHK_TOK(strtok_r(NULL, " \t", tok_ctx), "h boundary name"); + ERR(str_set(&desc->d.h_boundary.name, tk)); + if(find_description_by_name(stardis, &desc->d.h_boundary.name, NULL) + != desc) + { + logger_print(stardis->logger, LOG_ERROR, + "Name already used: %s\n", tk); + if(res == RES_OK) res = RES_BAD_ARG; + goto end; + } + + CHK_TOK(strtok_r(NULL, " \t", tok_ctx), "emissivity"); + res = cstr_to_double(tk, &desc->d.h_boundary.emissivity); + if(res != RES_OK + || desc->d.h_boundary.emissivity < 0 + || desc->d.h_boundary.emissivity > 1) + { + logger_print(stardis->logger, LOG_ERROR, "Invalid emissivity: %s\n", tk); + if(res == RES_OK) res = RES_BAD_ARG; + goto end; + } + CHK_TOK(strtok_r(NULL, " \t", tok_ctx), "specular fraction"); + res = cstr_to_double(tk, &desc->d.h_boundary.specular_fraction); + if(res != RES_OK + || desc->d.h_boundary.specular_fraction < 0 + || desc->d.h_boundary.specular_fraction > 1) + { + logger_print(stardis->logger, LOG_ERROR, + "Invalid specular fraction: %s\n", tk); + if(res == RES_OK) res = RES_BAD_ARG; + goto end; + } + CHK_TOK(strtok_r(NULL, " \t", tok_ctx), "hc"); + res = cstr_to_double(tk, &desc->d.h_boundary.hc); + if(res != RES_OK + || desc->d.h_boundary.hc < 0) + { + logger_print(stardis->logger, LOG_ERROR, "Invalid hc: %s\n", tk); + if(res == RES_OK) res = RES_BAD_ARG; + goto end; + } + CHK_TOK(strtok_r(NULL, " \t", tok_ctx), "temperature"); + res = cstr_to_double(tk, &desc->d.h_boundary.imposed_temperature); + if(res != RES_OK + || desc->d.h_boundary.imposed_temperature < 0) + { + logger_print(stardis->logger, LOG_ERROR, "Invalid temperature: %s\n", tk); + if(res == RES_OK) res = RES_BAD_ARG; + goto end; + } + + if(type == DESC_BOUND_H_FOR_FLUID) + desc->d.h_boundary.mat_id = get_dummy_solid_id(stardis, dummies); + else { + struct fluid fluid_props; + init_fluid(stardis->allocator, &fluid_props); + fluid_props.fluid_id = allocate_stardis_medium_id(stardis); + desc->d.h_boundary.mat_id = fluid_props.fluid_id; + ASSERT(sz <= UINT_MAX); + fluid_props.desc_id = (unsigned)sz; + fluid_props.imposed_temperature + = desc->d.h_boundary.imposed_temperature; + fluid_props.is_outside = 1; + fluid_props.is_green = stardis->mode & (MODE_BIN_GREEN | MODE_GREEN); + ERR(create_solver_fluid(stardis, &fluid_props)); + logger_print(stardis->logger, LOG_OUTPUT, + "External fluid created: T=%g (it is medium %u)\n", + fluid_props.imposed_temperature, + fluid_props.fluid_id); + } + + ASSERT(sz <= UINT_MAX); + ERR(read_sides_and_files(stardis, 1, (unsigned)sz, tok_ctx)); + +end: + return res; +error: + goto end; +} + +/* T_BOUNDARY_FOR_SOLID Name T STL_filenames + * T_BOUNDARY_FOR_FLUID Name T emissivity specular_fraction hc STL_filenames */ +static res_T +process_t + (struct stardis* stardis, + struct dummies* dummies, + const enum description_type type, + char** tok_ctx) +{ + char* tk = NULL; + struct description* desc; + size_t sz; + res_T res = RES_OK; + + ASSERT(stardis && tok_ctx); + + stardis->counts.tbound_count++; + + sz = darray_descriptions_size_get(&stardis->descriptions); + ERR(darray_descriptions_resize(&stardis->descriptions, sz + 1)); + desc = darray_descriptions_data_get(&stardis->descriptions) + sz; + init_t(stardis->allocator, &desc->d.t_boundary); + + desc->type = type; + desc->d.t_boundary.mat_id = (type == DESC_BOUND_T_FOR_FLUID) + ? get_dummy_solid_id(stardis, dummies) : get_dummy_fluid_id(stardis, dummies); + + CHK_TOK(strtok_r(NULL, " \t", tok_ctx), "temperature boundary name"); + ERR(str_set(&desc->d.t_boundary.name, tk)); + if(find_description_by_name(stardis, &desc->d.t_boundary.name, NULL) + != desc) + { + logger_print(stardis->logger, LOG_ERROR, + "Name already used: %s\n", tk); + if(res == RES_OK) res = RES_BAD_ARG; + goto end; + } + + CHK_TOK(strtok_r(NULL, " \t", tok_ctx), "temperature"); + res = cstr_to_double(tk, &desc->d.t_boundary.imposed_temperature); + if(res != RES_OK + || desc->d.t_boundary.imposed_temperature < 0) + { + logger_print(stardis->logger, LOG_ERROR, "Invalid temperature: %s\n", tk); + if(res == RES_OK) res = RES_BAD_ARG; + goto end; + } + if(type == DESC_BOUND_T_FOR_FLUID) { + CHK_TOK(strtok_r(NULL, " \t", tok_ctx), "emissivity"); + res = cstr_to_double(tk, &desc->d.t_boundary.emissivity); + if(res != RES_OK + || desc->d.t_boundary.emissivity < 0 + || desc->d.t_boundary.emissivity > 1) + { + logger_print(stardis->logger, LOG_ERROR, "Invalid emissivity: %s\n", tk); + if(res == RES_OK) res = RES_BAD_ARG; + goto end; + } + CHK_TOK(strtok_r(NULL, " \t", tok_ctx), "specular fraction"); + res = cstr_to_double(tk, &desc->d.t_boundary.specular_fraction); + if(res != RES_OK + || desc->d.t_boundary.specular_fraction < 0 + || desc->d.t_boundary.specular_fraction > 1) + { + logger_print(stardis->logger, LOG_ERROR, + "Invalid specular fraction: %s\n", tk); + if(res == RES_OK) res = RES_BAD_ARG; + goto end; + } + CHK_TOK(strtok_r(NULL, " \t", tok_ctx), "hc"); + res = cstr_to_double(tk, &desc->d.t_boundary.hc); + if(res != RES_OK + || desc->d.t_boundary.hc < 0) + { + logger_print(stardis->logger, LOG_ERROR, "Invalid hc: %s\n", tk); + if(res == RES_OK) res = RES_BAD_ARG; + goto end; + } + } + + ASSERT(sz <= UINT_MAX); + ERR(read_sides_and_files(stardis, 1, (unsigned)sz, tok_ctx)); + +end: + return res; +error: + goto end; +} + +/* F_BOUNDARY_FOR_SOLID Name F STL_filenames */ +static res_T +process_flx + (struct stardis* stardis, + struct dummies* dummies, + char** tok_ctx) +{ + char* tk = NULL; + struct description* desc; + size_t sz; + res_T res = RES_OK; + + ASSERT(stardis && tok_ctx); + + stardis->counts.fbound_count++; + + sz = darray_descriptions_size_get(&stardis->descriptions); + ERR(darray_descriptions_resize(&stardis->descriptions, sz + 1)); + desc = darray_descriptions_data_get(&stardis->descriptions) + sz; + init_f(stardis->allocator, &desc->d.f_boundary); + + desc->type = DESC_BOUND_F_FOR_SOLID; + desc->d.f_boundary.mat_id = get_dummy_fluid_id(stardis, dummies); + + CHK_TOK(strtok_r(NULL, " \t", tok_ctx), "flux boundary name"); + ERR(str_set(&desc->d.f_boundary.name, tk)); + if(find_description_by_name(stardis, &desc->d.f_boundary.name, NULL) + != desc) + { + logger_print(stardis->logger, LOG_ERROR, + "Name already used: %s\n", tk); + if(res == RES_OK) res = RES_BAD_ARG; + goto end; + } + + CHK_TOK(strtok_r(NULL, " \t", tok_ctx), "flux"); + res = cstr_to_double(tk, &desc->d.f_boundary.imposed_flux); + if(res != RES_OK + || desc->d.f_boundary.imposed_flux == SDIS_FLUX_NONE) { + /* Flux can be < 0 but not undefined */ + if(res == RES_OK) res = RES_BAD_ARG; + logger_print(stardis->logger, LOG_ERROR, "Invalid flux: %s\n", tk); + goto end; + } + + ASSERT(sz <= UINT_MAX); + ERR(read_sides_and_files(stardis, 1, (unsigned)sz, tok_ctx)); + +end: + return res; +error: + goto end; +} + +/* SOLID_FLUID_CONNECTION Name emissivity specular_fraction hc STL_filenames */ +static res_T +process_sfc + (struct stardis* stardis, + char** tok_ctx) +{ + char* tk = NULL; + struct description* desc; + size_t sz; + res_T res = RES_OK; + + ASSERT(stardis && tok_ctx); + + stardis->counts.sfconnect_count++; + + sz = darray_descriptions_size_get(&stardis->descriptions); + ERR(darray_descriptions_resize(&stardis->descriptions, sz + 1)); + desc = darray_descriptions_data_get(&stardis->descriptions) + sz; + init_sf(stardis->allocator, &desc->d.sf_connect); + + /* Use a medium ID even if there is no medium here + * As other cases use media IDs as unique IDs for read_sides_and_files calls + * we continue the trend to ensure connection ID is OK */ + desc->type = DESC_SOLID_FLUID_CONNECT; + desc->d.sf_connect.connection_id = allocate_stardis_medium_id(stardis); + + CHK_TOK(strtok_r(NULL, " \t", tok_ctx), "solid fluid connection name"); + ERR(str_set(&desc->d.sf_connect.name, tk)); + if(find_description_by_name(stardis, &desc->d.sf_connect.name, NULL) + != desc) + { + logger_print(stardis->logger, LOG_ERROR, + "Name already used: %s\n", tk); + if(res == RES_OK) res = RES_BAD_ARG; + goto end; + } + + CHK_TOK(strtok_r(NULL, " \t", tok_ctx), "emissivity"); + res = cstr_to_double(tk, &desc->d.sf_connect.emissivity); + if(res != RES_OK + || desc->d.sf_connect.emissivity < 0 + || desc->d.h_boundary.emissivity > 1) + { + logger_print(stardis->logger, LOG_ERROR, "Invalid emissivity: %s\n", tk); + if(res == RES_OK) res = RES_BAD_ARG; + goto end; + } + CHK_TOK(strtok_r(NULL, " \t", tok_ctx), "specular fraction"); + res = cstr_to_double(tk, &desc->d.sf_connect.specular_fraction); + if(res != RES_OK + || desc->d.sf_connect.specular_fraction < 0 + || desc->d.sf_connect.specular_fraction > 1) + { + logger_print(stardis->logger, LOG_ERROR, + "Invalid specular fraction: %s\n", tk); + if(res == RES_OK) res = RES_BAD_ARG; + goto end; + } + CHK_TOK(strtok_r(NULL, " \t", tok_ctx), "hc"); + res = cstr_to_double(tk, &desc->d.sf_connect.hc); + if(res != RES_OK + || desc->d.sf_connect.hc < 0) + { + logger_print(stardis->logger, LOG_ERROR, "Invalid hc: %s\n", tk); + if(res == RES_OK) res = RES_BAD_ARG; + goto end; + } + + ASSERT(sz <= UINT_MAX); + ERR(read_sides_and_files(stardis, 1, (unsigned)sz, tok_ctx)); + +end: + return res; +error: + goto end; +} + +static res_T +read_imposed_temperature + (struct stardis* stardis, + double* imposed_temperature, + char** tok_ctx) +{ + char* tk = NULL; + struct str keep; + res_T res = RES_OK; + ASSERT(stardis && imposed_temperature && tok_ctx); + + str_init(stardis->allocator, &keep); + CHK_TOK(strtok_r(NULL, " \t", tok_ctx), "imposed temperature"); + ERR(str_set(&keep, tk)); + if(RES_OK == cstr_to_double(tk, imposed_temperature)) { + /* Was a number */ + if(*imposed_temperature < 0) { + res = RES_BAD_ARG; + goto error; + } + } else { + /* Could be 'unknown' */ + _strupr(tk); + if(0 == strcmp(tk, "UNKNOWN")) { + *imposed_temperature = UNKNOWN_MEDIUM_TEMPERATURE; + } else { + res = RES_BAD_ARG; + goto error; + } + } +end: + str_release(&keep); + return res; +error: + logger_print(stardis->logger, LOG_ERROR, "Invalid imposed temperature: %s\n", + str_cget(&keep)); + goto end; +} + +static res_T +read_delta + (struct stardis* stardis, + double* delta, + char** tok_ctx) +{ + char* tk = NULL; + struct str keep; + res_T res = RES_OK; + ASSERT(stardis && delta && tok_ctx); + + str_init(stardis->allocator, &keep); + CHK_TOK(strtok_r(NULL, " \t", tok_ctx), "delta"); + ERR(str_set(&keep, tk)); + if(RES_OK == cstr_to_double(tk, delta)) { + /* Was a number */ + if(*delta <= 0) { + res = RES_BAD_ARG; + goto error; + } + } else { + /* Could be 'auto' */ + _strupr(tk); + if(0 == strcmp(tk, "AUTO")) { + /* Set to DELTA_AUTO until actual value is substituted */ + *delta = DELTA_AUTO; + } else { + res = RES_BAD_ARG; + goto error; + } + } +end: + str_release(&keep); + return res; +error: + logger_print(stardis->logger, LOG_ERROR, "Invalid delta: %s\n", + str_cget(&keep)); + goto end; +} + +/* SOLID Name lambda rho cp delta Tinit Timposed volumic_power STL_filenames */ +static res_T +process_solid + (struct stardis* stardis, + char** tok_ctx) +{ + char* tk = NULL; + struct description* desc; + size_t sz; + res_T res = RES_OK; + + ASSERT(stardis && tok_ctx); + + stardis->counts.smed_count++; + + sz = darray_descriptions_size_get(&stardis->descriptions); + ERR(darray_descriptions_resize(&stardis->descriptions, sz + 1)); + desc = darray_descriptions_data_get(&stardis->descriptions) + sz; + init_solid(stardis->allocator, &desc->d.solid); + + desc->type = DESC_MAT_SOLID; + desc->d.solid.solid_id = allocate_stardis_medium_id(stardis); + desc->d.solid.is_green = stardis->mode & (MODE_BIN_GREEN | MODE_GREEN); + desc->d.solid.is_outside = 0; + ASSERT(sz <= UINT_MAX); + desc->d.solid.desc_id = (unsigned)sz; + + CHK_TOK(strtok_r(NULL, " \t", tok_ctx), "solid name"); + ERR(str_set(&desc->d.solid.name, tk)); + if(find_description_by_name(stardis, &desc->d.solid.name, NULL) + != desc) + { + logger_print(stardis->logger, LOG_ERROR, + "Name already used: %s\n", tk); + if(res == RES_OK) res = RES_BAD_ARG; + goto end; + } + + CHK_TOK(strtok_r(NULL, " \t", tok_ctx), "lambda"); + res = cstr_to_double(tk, &desc->d.solid.lambda); + if(res != RES_OK + || desc->d.solid.lambda <= 0) + { + logger_print(stardis->logger, LOG_ERROR, "Invalid lambda: %s\n", tk); + if(res == RES_OK) res = RES_BAD_ARG; + goto end; + } + CHK_TOK(strtok_r(NULL, " \t", tok_ctx), "rho"); + res = cstr_to_double(tk, &desc->d.solid.rho); + if(res != RES_OK + || desc->d.solid.rho <= 0) + { + logger_print(stardis->logger, LOG_ERROR, "Invalid rho: %s\n", tk); + if(res == RES_OK) res = RES_BAD_ARG; + goto end; + } + CHK_TOK(strtok_r(NULL, " \t", tok_ctx), "cp"); + res = cstr_to_double(tk, &desc->d.solid.cp); + if(res != RES_OK + || desc->d.solid.cp <= 0) + { + logger_print(stardis->logger, LOG_ERROR, "Invalid cp: %s\n", tk); + if(res == RES_OK) res = RES_BAD_ARG; + goto end; + } + ERR(read_delta(stardis, &desc->d.solid.delta, tok_ctx)); + CHK_TOK(strtok_r(NULL, " \t", tok_ctx), "Tinit"); + res = cstr_to_double(tk, &desc->d.solid.tinit); + if(res != RES_OK + || desc->d.solid.tinit < 0) + { + logger_print(stardis->logger, LOG_ERROR, "Invalid Tinit: %s\n", tk); + if(res == RES_OK) res = RES_BAD_ARG; + goto end; + } + ERR(read_imposed_temperature(stardis, &desc->d.solid.imposed_temperature, + tok_ctx)); + if(desc->d.solid.imposed_temperature >= 0 + && desc->d.solid.imposed_temperature != desc->d.solid.tinit) + { + logger_print(stardis->logger, LOG_ERROR, + "Imposed temperature, if defined, must match initial temperature " + "(initial: %g; imposed: %g)\n", + desc->d.solid.tinit, desc->d.solid.imposed_temperature); res = RES_BAD_ARG; + goto end; + } + CHK_TOK(strtok_r(NULL, " \t", tok_ctx), "volumic power"); + res = cstr_to_double(tk, &desc->d.solid.vpower); + if(res != RES_OK) { + /* VPower can be < 0 */ + logger_print(stardis->logger, LOG_ERROR, "Invalid volumic power: %s\n", tk); + goto end; + } + + /* Actual solid creation is defered until geometry is read to allow + * enclosure shape VS delta analysis (and auto delta computation) */ + + ASSERT(sz <= UINT_MAX); + ERR(read_sides_and_files(stardis, 0, (unsigned)sz, tok_ctx)); + +end: + return res; +error: + goto end; +} + +/* FLUID Name rho cp Tinit Timposed STL_filenames */ +static res_T +process_fluid + (struct stardis* stardis, + char** tok_ctx) +{ + char* tk = NULL; + struct description* desc; + size_t sz; + res_T res = RES_OK; + + ASSERT(stardis && tok_ctx); + + stardis->counts.fmed_count++; + + sz = darray_descriptions_size_get(&stardis->descriptions); + ERR(darray_descriptions_resize(&stardis->descriptions, sz + 1)); + desc = darray_descriptions_data_get(&stardis->descriptions) + sz; + init_fluid(stardis->allocator, &desc->d.fluid); + + desc->type = DESC_MAT_FLUID; + desc->d.fluid.fluid_id = allocate_stardis_medium_id(stardis); + desc->d.fluid.is_outside = 0; + desc->d.fluid.is_green = stardis->mode & (MODE_BIN_GREEN | MODE_GREEN); + ASSERT(sz <= UINT_MAX); + desc->d.fluid.desc_id = (unsigned)sz; + + CHK_TOK(strtok_r(NULL, " \t", tok_ctx), "fluid name"); + ERR(str_set(&desc->d.fluid.name, tk)); + if(find_description_by_name(stardis, &desc->d.fluid.name, NULL) + != desc) + { + logger_print(stardis->logger, LOG_ERROR, + "Name already used: %s\n", tk); + if(res == RES_OK) res = RES_BAD_ARG; + goto end; + } + + CHK_TOK(strtok_r(NULL, " \t", tok_ctx), "rho"); + res = cstr_to_double(tk, &desc->d.fluid.rho); + if(res != RES_OK + || desc->d.fluid.rho <= 0) + { + logger_print(stardis->logger, LOG_ERROR, "Invalid rho: %s\n", tk); + if(res == RES_OK) res = RES_BAD_ARG; + goto end; + } + CHK_TOK(strtok_r(NULL, " \t", tok_ctx), "cp"); + res = cstr_to_double(tk, &desc->d.fluid.cp); + if(res != RES_OK + || desc->d.fluid.cp <= 0) + { + logger_print(stardis->logger, LOG_ERROR, "Invalid cp: %s\n", tk); + if(res == RES_OK) res = RES_BAD_ARG; + goto end; + } + CHK_TOK(strtok_r(NULL, " \t", tok_ctx), "Tinit"); + res = cstr_to_double(tk, &desc->d.fluid.tinit); + if(res != RES_OK + || desc->d.fluid.tinit < 0) + { + logger_print(stardis->logger, LOG_ERROR, "Invalid Tinit: %s\n", tk); + if(res == RES_OK) res = RES_BAD_ARG; + goto end; + } + + ERR(read_imposed_temperature(stardis, &desc->d.fluid.imposed_temperature, + tok_ctx)); + if(desc->d.fluid.imposed_temperature >= 0 + && desc->d.fluid.imposed_temperature != desc->d.fluid.tinit) + { + logger_print(stardis->logger, LOG_ERROR, + "Imposed temperature, if defined, must match initial temperature " + "(initial: %g; imposed: %g)\n", + desc->d.fluid.tinit, desc->d.fluid.imposed_temperature); res = RES_BAD_ARG; + goto end; + } + + ERR(create_solver_fluid(stardis, &desc->d.fluid)); + + ASSERT(sz <= UINT_MAX); + ERR(read_sides_and_files(stardis, 0, (unsigned)sz, tok_ctx)); + +end: + return res; +error: + goto end; +} + +/* SCALE scale_factor */ +static res_T +process_scale + (struct stardis* stardis, + char** tok_ctx) +{ + char* tk = NULL; + res_T res = RES_OK; + + ASSERT(stardis && tok_ctx); + + if(stardis->scale_factor > 0) { + logger_print(stardis->logger, LOG_ERROR, + "SCALE cannot be specified twice\n"); + res = RES_BAD_ARG; + goto end; + } + CHK_TOK(strtok_r(NULL, " \t", tok_ctx), "scale factor"); + res = cstr_to_double(tk, &stardis->scale_factor); + if(res != RES_OK + || stardis->scale_factor <= 0) + { + logger_print(stardis->logger, LOG_ERROR, + "Invalid scale factor: %s\n", tk); + if(res == RES_OK) res = RES_BAD_ARG; + goto end; + } + +end: + return res; +error: + goto end; +} + +/* Read medium or boundary line; should be one of: + * SOLID Name lambda rho cp delta Tinit Timposed volumic_power STL_filenames + * FLUID Name rho cp Tinit Timposed STL_filenames + * H_BOUNDARY_FOR_SOLID Name emissivity specular_fraction hc T_env STL_filenames + * H_BOUNDARY_FOR_FLUID Name emissivity specular_fraction hc T_env STL_filenames + * T_BOUNDARY_FOR_SOLID Name T STL_filenames + * T_BOUNDARY_FOR_FLUID Name T hc STL_filenames + * F_BOUNDARY_FOR_SOLID Name F STL_filenames + * SOLID_FLUID_CONNECTION Name emissivity specular_fraction hc STL_filenames + * SCALE scale_factor + * + * STL_filenames = { { FRONT | BACK | BOTH } STL_filename }+ + */ +res_T +process_model_line + (const char* file_name, + char* line, + struct stardis* stardis, + struct dummies* dummies) +{ + res_T res = RES_OK; + struct str keep; + char* tk = NULL, * tok_ctx = NULL; + + ASSERT(file_name && line && stardis); + + str_init(stardis->allocator, &keep); + ERR(str_set(&keep, line)); + CHK_TOK(strtok_r(line, " \t", &tok_ctx), "model line type"); + _strupr(tk); + + if(0 == strcmp(tk, "H_BOUNDARY_FOR_SOLID")) + ERR(process_h(stardis, dummies, DESC_BOUND_H_FOR_SOLID, &tok_ctx)); + else if(0 == strcmp(tk, "H_BOUNDARY_FOR_FLUID")) + ERR(process_h(stardis, dummies, DESC_BOUND_H_FOR_FLUID, &tok_ctx)); + else if(0 == strcmp(tk, "T_BOUNDARY_FOR_SOLID")) + ERR(process_t(stardis, dummies, DESC_BOUND_T_FOR_SOLID, &tok_ctx)); + else if(0 == strcmp(tk, "T_BOUNDARY_FOR_FLUID")) + ERR(process_t(stardis, dummies, DESC_BOUND_T_FOR_FLUID, &tok_ctx)); + else if(0 == strcmp(tk, "F_BOUNDARY_FOR_SOLID")) + ERR(process_flx(stardis, dummies, &tok_ctx)); + else if(0 == strcmp(tk, "SOLID_FLUID_CONNECTION")) + ERR(process_sfc(stardis, &tok_ctx)); + else if(0 == strcmp(tk, "SOLID")) + ERR(process_solid(stardis, &tok_ctx)); + else if(0 == strcmp(tk, "FLUID")) + ERR(process_fluid(stardis, &tok_ctx)); + else if(0 == strcmp(tk, "SCALE")) + ERR(process_scale(stardis, &tok_ctx)); + else { + logger_print(stardis->logger, LOG_ERROR, + "Unknown description type: %s\n", tk); + res = RES_BAD_ARG; + goto error; + } + +end: + str_release(&keep); + return res; +error: + logger_print(stardis->logger, LOG_ERROR, + "Invalid description line in model file '%s':\n", file_name); + logger_print(stardis->logger, LOG_ERROR, "%s\n", str_cget(&keep)); + goto end; +} + +unsigned +get_dummy_solid_id + (struct stardis* stardis, + struct dummies* dummies) +{ + struct solid dummy; + if(dummies->dummy_solid) + return dummies->dummy_solid_id; + dummies->dummy_solid_id = allocate_stardis_medium_id(stardis); + init_solid(stardis->allocator, &dummy); + dummy.solid_id = dummies->dummy_solid_id; + dummy.is_outside = 1; + dummy.is_green = stardis->mode & (MODE_BIN_GREEN | MODE_GREEN); + create_solver_solid(stardis, &dummy); + dummies->dummy_solid + = darray_media_ptr_data_get(&stardis->media)[dummies->dummy_solid_id]; + logger_print(stardis->logger, LOG_OUTPUT, + "Dummy solid created: (it is medium %u)\n", + dummies->dummy_solid_id); + return dummies->dummy_solid_id; +} + +unsigned +get_dummy_fluid_id + (struct stardis* stardis, + struct dummies* dummies) +{ + struct fluid dummy; + if(dummies->dummy_fluid) + return dummies->dummy_fluid_id; + dummies->dummy_fluid_id = allocate_stardis_medium_id(stardis); + init_fluid(stardis->allocator, &dummy); + dummy.fluid_id = dummies->dummy_fluid_id; + dummy.is_outside = 1; + dummy.is_green = stardis->mode & (MODE_BIN_GREEN | MODE_GREEN); + create_solver_fluid(stardis, &dummy); + dummies->dummy_fluid + = darray_media_ptr_data_get(&stardis->media)[dummies->dummy_fluid_id]; + logger_print(stardis->logger, LOG_OUTPUT, + "Dummy fluid created: (it is medium %u)\n", + dummies->dummy_fluid_id); + return dummies->dummy_fluid_id; +} diff --git a/src/stardis-parsing.h b/src/stardis-parsing.h @@ -0,0 +1,203 @@ +/* Copyright (C) 2018-2020 |Meso|Star> (contact@meso-star.com) + * + * This program 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. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef STARDIS_PARSING_H +#define STARDIS_PARSING_H + +#include <sdis.h> + +#include <rsys/rsys.h> +#include <rsys/dynamic_array_str.h> + +#include <star/sstl.h> + +struct camera; +struct logger; +struct mem_allocator; +struct stardis; +struct dummies; + +/* Utility macros */ +#define CHK_TOK(x, Name) if((tk = (x)) == NULL) {\ + logger_print(stardis->logger, LOG_ERROR,\ + "Invalid data (missing token '" Name "')\n");\ + res = RES_BAD_ARG;\ + goto error;\ + } (void)0 + +#ifdef COMPILER_CL +#define strtok_r strtok_s +#endif + +enum stardis_mode { + /* Ordered so that print_multiple_modes() prints in alphabetical order */ + UNDEF_MODE = 0, + MODE_DUMP_C_CHUNKS = BIT(0), /* -c */ + MODE_DUMP_PATHS = BIT(1), /* -D */ + MODE_DUMP_VTK = BIT(2), /* -d */ + MODE_EXTENDED_RESULTS = BIT(3), /* -e */ + MODE_FLUX_BOUNDARY_COMPUTE = BIT(4), /* -F */ + MODE_BIN_GREEN = BIT(5), /* -G */ + MODE_GREEN = BIT(6), /* -g */ + MODE_DUMP_HELP = BIT(7), /* -h */ + MODE_MEDIUM_COMPUTE = BIT(8), /* -m */ + MODE_PROBE_COMPUTE_ON_INTERFACE = BIT(9), /* -P */ + MODE_PROBE_COMPUTE = BIT(10), /* -p */ + MODE_IR_COMPUTE = BIT(11), /* -R */ + MODE_MAP_COMPUTE = BIT(12), /* -S */ + MODE_BOUNDARY_COMPUTE = BIT(13), /* -s */ + MODE_VERBOSITY = BIT(14), /* -V */ + MODE_DUMP_VERSION = BIT(15), /* -v */ + + GREEN_COMPATIBLE_MODES + = MODE_PROBE_COMPUTE | MODE_PROBE_COMPUTE_ON_INTERFACE | MODE_MEDIUM_COMPUTE + | MODE_BOUNDARY_COMPUTE, + + SURFACE_COMPUTE_MODES + = MODE_BOUNDARY_COMPUTE | MODE_FLUX_BOUNDARY_COMPUTE | MODE_MAP_COMPUTE, + + EXT_COMPATIBLE_MODES + = GREEN_COMPATIBLE_MODES | MODE_MEDIUM_COMPUTE | MODE_FLUX_BOUNDARY_COMPUTE, + + REGION_COMPUTE_MODES = SURFACE_COMPUTE_MODES | MODE_MEDIUM_COMPUTE, + + COMPUTE_MODES = GREEN_COMPATIBLE_MODES | MODE_IR_COMPUTE | SURFACE_COMPUTE_MODES, + + EXCLUSIVE_MODES = COMPUTE_MODES, + + SHORT_EXIT_MODES = MODE_DUMP_HELP | MODE_DUMP_VERSION, + + USE_STDOUT_MODES + = MODE_DUMP_C_CHUNKS | MODE_DUMP_VTK | MODE_DUMP_HELP | MODE_DUMP_VERSION + | MODE_IR_COMPUTE | MODE_GREEN +}; + +STATIC_ASSERT(GREEN_COMPATIBLE_MODES == (COMPUTE_MODES & GREEN_COMPATIBLE_MODES), + Cannot_have_a_GREEN_COMPATIBLE_MODE_that_is_not_a_COMPUTE_MODE); + +enum dump_path_type { + DUMP_NONE = 0, + DUMP_SUCCESS = BIT(0), + DUMP_ERROR = BIT(1), + DUMP_ALL = DUMP_SUCCESS | DUMP_ERROR +}; + +struct args { + struct logger* logger; + struct mem_allocator* allocator; + struct darray_str model_files; + char* medium_name; + char* solve_filename; + char* bin_green_filename; + char* end_paths_filename; + char* paths_filename; + char* chunks_prefix; + size_t samples; + unsigned nthreads; + double pos_and_time[5]; + enum stardis_mode mode; + double ambient_temp, ref_temp; + char* camera; + enum dump_path_type dump_paths; + int verbose; +}; + +/* Same ctx used for both media and interface add (some unused parts) */ +struct add_geom_ctx { + struct sstl_desc stl_desc; + unsigned properties[3]; + void* custom; +}; + +/* Possible callbacks for sg3d_geometry_add calls + * when void* context is a struct add_geom_ctx */ +extern LOCAL_SYM void +add_geom_ctx_indices + (const unsigned itri, + unsigned ids[3], + void* context); + +extern LOCAL_SYM void +add_geom_ctx_properties + (const unsigned itri, + unsigned prop[3], + void* context); + +extern LOCAL_SYM void +add_geom_ctx_position + (const unsigned ivert, + double pos[3], + void* context); + +extern LOCAL_SYM res_T +init_args + (struct logger* logger, + struct mem_allocator* mem, + struct args** args); + +extern LOCAL_SYM void +release_args + (struct args* args); + +extern char +mode_option + (const int m); + +extern void +print_multiple_modes + (char* buf, + const size_t sz, + const int modes, + const int dont); + +extern void +print_version + (FILE* stream); + +extern void +short_help + (FILE* stream, + const char* prog); + +extern res_T +parse_args + (const int argc, + char** argv, + struct args* args); + +extern res_T +parse_camera + (struct logger* logger, + char* cam_param, + struct stardis* stardis); + +extern LOCAL_SYM res_T +process_model_line + (const char* file_name, + char* line, + struct stardis* stardis, + struct dummies* dummies); + +extern LOCAL_SYM unsigned +get_dummy_solid_id + (struct stardis* stardis, + struct dummies* dummies); + +extern LOCAL_SYM unsigned +get_dummy_fluid_id + (struct stardis* stardis, + struct dummies* dummies); + +#endif /*ARGS_H*/ diff --git a/src/stardis-solid.c b/src/stardis-solid.c @@ -0,0 +1,221 @@ +/* Copyright (C) 2018-2020 |Meso|Star> (contact@meso-star.com) + * + * This program 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. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "stardis-solid.h" +#include "stardis-compute.h" +#include "stardis-app.h" + +#include <sdis.h> + +#include <limits.h> + +/******************************************************************************* + * Local Functions + ******************************************************************************/ + +static double +solid_get_calorific_capacity + (const struct sdis_rwalk_vertex* vtx, + struct sdis_data* data) +{ + const struct solid* solid_props = sdis_data_cget(data); + (void)vtx; + return solid_props->cp; +} + +static double +solid_get_thermal_conductivity + (const struct sdis_rwalk_vertex* vtx, + struct sdis_data* data) +{ + const struct solid* solid_props = sdis_data_cget(data); + (void)vtx; + return solid_props->lambda; +} + +static double +solid_get_volumic_mass + (const struct sdis_rwalk_vertex* vtx, + struct sdis_data* data) +{ + const struct solid* solid_props = sdis_data_cget(data); + (void)vtx; + return solid_props->rho; +} + +static double +solid_get_delta + (const struct sdis_rwalk_vertex* vtx, + struct sdis_data* data) +{ + const struct solid* solid_props = sdis_data_cget(data); + (void)vtx; + return solid_props->delta; +} + +#if Stardis_VERSION_MINOR == 3 +static double +solid_get_delta_boundary + (const struct sdis_rwalk_vertex* vtx, + struct sdis_data* data) +{ + const struct solid* solid_props = sdis_data_cget(data); + return solid_props->delta; +} +#endif + +static double +solid_get_temperature + (const struct sdis_rwalk_vertex* vtx, + struct sdis_data* data) +{ + const struct solid* solid_props = sdis_data_cget(data); + if(solid_props->imposed_temperature >= 0) + /* If there is an imposed temp, it is imposed regardless of time */ + return solid_props->imposed_temperature; + if(vtx->time <= solid_props->t0) { + /* If time is <= t0: use tinit */ + if(solid_props->tinit < 0) { + if(str_is_empty(&solid_props->name)) { + FATAL("solid_get_temperature: getting undefined Tinit\n"); + } else { + VFATAL("solid_get_temperature: getting undefined Tinit (solid '%s')\n", + ARG1(str_cget(&solid_props->name))); + } + } + return solid_props->tinit; + } + return -1; /* Unknown temperature */ +} + +static double +solid_get_power + (const struct sdis_rwalk_vertex* vtx, + struct sdis_data* data) +{ + const struct solid* solid_props = sdis_data_cget(data); + (void)vtx; + return solid_props->vpower; +} + +/******************************************************************************* + * Public Functions + ******************************************************************************/ + +res_T +create_solver_solid + (struct stardis* stardis, + const struct solid* solid_props) +{ + res_T res = RES_OK; + struct sdis_solid_shader solid_shader = SDIS_SOLID_SHADER_NULL; + struct sdis_data* data = NULL; + struct solid* props; + /* Could be less restrictive if green output included positions/dates */ + + ASSERT(stardis && solid_props); + solid_shader.calorific_capacity = solid_get_calorific_capacity; + solid_shader.thermal_conductivity = solid_get_thermal_conductivity; + solid_shader.volumic_mass = solid_get_volumic_mass; + solid_shader.delta_solid = solid_get_delta; +#if Stardis_VERSION_MINOR == 3 + solid_shader.delta_boundary = solid_get_delta_boundary; +#endif + solid_shader.temperature = solid_get_temperature; + ERR(sdis_data_create(stardis->dev, sizeof(struct solid), ALIGNOF(struct solid), + NULL, &data)); + + props = sdis_data_get(data); /* Fetch the allocated memory space */ + init_solid(stardis->allocator, props); + cp_solid(props, solid_props); + if(solid_props->vpower != 0) solid_shader.volumic_power = solid_get_power; + if(solid_props->solid_id >= darray_media_ptr_size_get(&stardis->media)) { + ERR(darray_media_ptr_resize(&stardis->media, solid_props->solid_id + 1)); + } + ASSERT(darray_media_ptr_data_get(&stardis->media)[solid_props->solid_id] == NULL); + ERR(sdis_solid_create(stardis->dev, &solid_shader, data, + darray_media_ptr_data_get(&stardis->media) + solid_props->solid_id)); + +end: + if(data) SDIS(data_ref_put(data)); + return res; +error: + goto end; +} + +void +init_solid(struct mem_allocator* allocator, struct solid* dst) +{ + str_init(allocator, &dst->name); + dst->lambda = 1; + dst->rho = 1; + dst->cp = 1; + dst->delta = 1; + dst->tinit = -1; + dst->imposed_temperature = -1; + dst->vpower = 0; + dst->t0 = 0; + dst->is_outside = 0; + dst->is_green = 0; + dst->desc_id = UINT_MAX; + dst->solid_id = UINT_MAX; +} + +void +release_solid(struct solid* solid) +{ + str_release(&solid->name); +} + +res_T +str_print_solid(struct str* str, const struct solid* s) +{ + res_T res = RES_OK; + ASSERT(str && s); + STR_APPEND_PRINTF(str, "Solid '%s': lambda=%g rho=%g cp=%g delta=%g", + ARG5( str_cget(&s->name), s->lambda, s->rho, s->cp, s->delta ) ); + if(s->vpower != 0) { + STR_APPEND_PRINTF(str, " VPower=%g", ARG1( s->vpower ) ); + } + if(s->tinit >= 0) { + STR_APPEND_PRINTF(str, " Tinit=%g", ARG1( s->tinit ) ); + } + if(s->imposed_temperature >= 0) { + STR_APPEND_PRINTF(str, " Temp=%g", ARG1( s->imposed_temperature ) ); + } + STR_APPEND_PRINTF(str, " (it is medium %u)", ARG1( s->solid_id ) ); +end: + return res; +error: + goto end; +} + +res_T +cp_solid(struct solid* dst, const struct solid* src) +{ + dst->lambda = src->lambda; + dst->rho = src->rho; + dst->cp = src->cp; + dst->delta = src->delta; + dst->tinit = src->tinit; + dst->imposed_temperature = src->imposed_temperature; + dst->vpower = src->vpower; + dst->t0 = src->t0; + dst->is_outside = src->is_outside; + dst->is_green = src->is_green; + dst->desc_id = src->desc_id; + dst->solid_id = src->solid_id; + return str_copy(&dst->name, &src->name); +} diff --git a/src/stardis-solid.h b/src/stardis-solid.h @@ -0,0 +1,60 @@ +/* Copyright (C) 2018-2020 |Meso|Star> (contact@meso-star.com) + * + * This program 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. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef SDIS_SOLID_H +#define SDIS_SOLID_H + +#include <rsys/rsys.h> +#include <rsys/str.h> + +struct stardis; + +/******************************************************************************* + * Solid data + ******************************************************************************/ +struct solid { + struct str name; + double lambda; /* Conductivity */ + double rho; /* Volumic mass */ + double cp; /* Calorific capacity */ + double delta; /* Numerical parameter */ + double tinit; /* Initial temperature */ + double imposed_temperature; /* allow to impose a T; -1 if unset */ + double vpower; + double t0; /* End time of tinit */ + int is_outside; /* the solid is used for a boundary */ + int is_green; /* green computation (nothing to do with solid itself) */ + unsigned desc_id; /* id of the boundary; meaningful if is_outside */ + unsigned solid_id; +}; + +LOCAL_SYM void +init_solid(struct mem_allocator* allocator, struct solid* dst); + +LOCAL_SYM void +release_solid(struct solid* solid); + +LOCAL_SYM res_T +str_print_solid(struct str* str, const struct solid* s); + +LOCAL_SYM res_T +cp_solid(struct solid* dst, const struct solid* src); + +LOCAL_SYM res_T +create_solver_solid + (struct stardis* stardis, + const struct solid* solid_props); + +#endif diff --git a/src/stardis-version.h.in b/src/stardis-version.h.in @@ -0,0 +1,24 @@ +/* Copyright (C) 2018-2020 |Meso|Star> + * + * This program 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. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef STARDIS_APP_VERSION_H +#define STARDIS_APP_VERSION_H + +#define STARDIS_APP_VERSION_MAJOR @SDIS_VERSION_MAJOR@ +#define STARDIS_APP_VERSION_MINOR @SDIS_VERSION_MINOR@ +#define STARDIS_APP_VERSION_PATCH @SDIS_VERSION_PATCH@ + +#endif /* STARDIS_APP_VERSION_H */ + diff --git a/tinyexpr/tinyexpr.c b/tinyexpr/tinyexpr.c @@ -1,890 +0,0 @@ -/* - * TINYEXPR - Tiny recursive descent parser and evaluation engine in C - * - * Copyright (c) 2015-2018 Lewis Van Winkle - * - * http://CodePlea.com - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgement in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -/* COMPILE TIME OPTIONS */ - -/* Exponentiation associativity: -For a^b^c = (a^b)^c and -a^b = (-a)^b do nothing. -For a^b^c = a^(b^c) and -a^b = -(a^b) uncomment the next line.*/ -/* #define TE_POW_FROM_RIGHT */ - -/* Logarithms -For log = base 10 log do nothing -For log = natural log uncomment the next line. */ -/* #define TE_NAT_LOG */ - -#include "tinyexpr.h" -#include <stdlib.h> -#include <math.h> -#include <string.h> -#include <stdio.h> -#include <limits.h> -#include <assert.h> - -#ifndef M_E -#define M_E 2.71828182845904523536 // e -#endif - -#ifndef M_PI -#define M_PI 3.14159265358979323846 // pi -#endif - -#ifndef NAN -#define NAN (0.0/0.0) -#endif - -#ifndef INFINITY -#define INFINITY (1.0/0.0) -#endif - - -typedef double (*te_fun2)(double, double); - -enum { - -#ifdef TE_WITHOUT_FUNCTION_0 - TE_FUNCTION0 = 8, -#endif -#ifdef TE_WITHOUT_CLOSURES - TE_CLOSURE7 = 23, -#endif - - TOK_NULL = TE_CLOSURE7+1, TOK_ERROR, TOK_END, TOK_SEP, - TOK_OPEN, TOK_CLOSE, TOK_NUMBER, TOK_VARIABLE, TOK_OFFSET, TOK_INFIX, - - TE_CONDITION = 256 -}; - -typedef struct state { - const char *start; - const char *next; - int type; - union value v; - void *context; - - const te_variable *lookup; - int lookup_len; -} state; - - -#define TYPE_MASK(TYPE) ((TYPE)&0x0000003F) - -#define IS_PURE(TYPE) (((TYPE) & TE_FLAG_PURE) != 0) -#define IS_FUNCTION(TYPE) (((TYPE) & TE_FUNCTION0) != 0) -#ifdef TE_WITHOUT_CLOSURES -#define ARITY(TYPE) ( ((TYPE) & TE_FUNCTION0) ? ((TYPE) & 0x00000007) : 0 ) -#else -#define IS_CLOSURE(TYPE) (((TYPE) & TE_CLOSURE0) != 0) -#define ARITY(TYPE) ( ((TYPE) & (TE_FUNCTION0 | TE_CLOSURE0)) ? ((TYPE) & 0x00000007) : 0 ) -#endif -#define IS_CONDITION(TYPE) (((TYPE) & TE_CONDITION) != 0) -#define NEW_EXPR(type, ...) new_expr((type), (const te_expr*[]){__VA_ARGS__}) - -static te_expr *new_expr(const int type, const te_expr *parameters[]) { - const size_t arity = ARITY(type); - const size_t psize = sizeof(void*) * arity; - const size_t size = (sizeof(te_expr) - sizeof(void*)) + psize -#ifndef TE_WITHOUT_CLOSURES - + (IS_CLOSURE(type) ? sizeof(void*) : 0) -#endif - ; - te_expr *ret = malloc(size); - memset(ret, 0, size); - if (arity && parameters) { - memcpy(ret->parameters, parameters, psize); - } - ret->type = type; - ret->v.bound = 0; - return ret; -} - -static te_expr *new_expr1(const int type, te_expr *p1) { - const size_t size = sizeof(te_expr) -#ifndef TE_WITHOUT_CLOSURES - + (IS_CLOSURE(type) ? sizeof(void*) : 0) -#endif - ; - assert(p1 && ARITY(type) == 1); - te_expr *ret = malloc(size); - ret->type = type; - ret->v.bound = 0; - ret->parameters[0] = p1; - return ret; -} - -te_expr* te_duplicate(te_expr* n) { - const size_t size = sizeof(te_expr) -#ifndef TE_WITHOUT_CLOSURES - + (IS_CLOSURE(n->type) ? sizeof(void*) : 0); -#endif - ; - te_expr* nn; - assert(n); - nn = malloc(size); - memcpy(nn, n, size); - return nn; -} - -static te_expr *new_expr2(const int type, te_expr *p1, te_expr *p2) { - const size_t size = sizeof(te_expr) + sizeof(void*) -#ifndef TE_WITHOUT_CLOSURES - + (IS_CLOSURE(type) ? sizeof(void*) : 0) -#endif - ; - assert(p1 && p2 && ARITY(type) == 2); - te_expr *ret = malloc(size); - ret->type = type; - ret->v.bound = 0; - ret->parameters[0] = p1; - ret->parameters[1] = p2; - return ret; -} - -static void te_free_parameters(te_expr *n) { - int i; - if (!n) return; - for (i = 0; i < ARITY(n->type); i++)te_free(n->parameters[i]); -} - - -void te_free(te_expr *n) { - if (!n) return; - te_free_parameters(n); - free(n); -} - -static double fac(double a) {/* simplest version of fac */ - if (a < 0.0) - return NAN; - if (a > UINT_MAX) - return INFINITY; - unsigned int ua = (unsigned int)(a); - unsigned long int result = 1, i; - for (i = 1; i <= ua; i++) { - if (i > ULONG_MAX / result) - return INFINITY; - result *= i; - } - return (double)result; -} -static double ncr(double n, double r) { - if (n < 0.0 || r < 0.0 || n < r) return NAN; - if (n > UINT_MAX || r > UINT_MAX) return INFINITY; - unsigned long int un = (unsigned int)(n), ur = (unsigned int)(r), i; - unsigned long int result = 1; - if (ur > un / 2) ur = un - ur; - for (i = 1; i <= ur; i++) { - if (result > ULONG_MAX / (un - ur + i)) - return INFINITY; - result *= un - ur + i; - result /= i; - } - return (double)result; -} -static double npr(double n, double r) {return ncr(n, r) * fac(r);} - -/* Workaround for a VC 2017 problem */ -static double ceil_(double x) { return ceil(x); } -static double floor_(double x) { return floor(x); } - -static const te_variable functions[] = { - /* must be in alphabetical order */ - {"abs", {.f1=fabs}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"acos", {.f1=acos}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"asin", {.f1=asin}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"atan", {.f1=atan}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"atan2", {.f2=atan2}, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"ceil", {.f1=ceil_}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"cos", {.f1=cos}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"cosh", {.f1=cosh}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"e", {.value = M_E}, TE_CONSTANT, 0}, - {"exp", {.f1=exp}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"fac", {.f1=fac}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"floor", {.f1=floor_}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"if", {.f3 = NULL}, /* Specific treatment, no associated C function */ - TE_FUNCTION3 | TE_CONDITION | TE_FLAG_PURE, 0}, - {"ln", {.f1=log}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, -#ifdef TE_NAT_LOG - {"log", {.f1=log}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, -#else - {"log", {.f1=log10}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, -#endif - {"log10", {.f1=log10}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"ncr", {.f2=ncr}, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"npr", {.f2=npr}, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"pi", {.value=M_PI}, TE_CONSTANT, 0}, - {"pow", {.f2=pow}, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"sin", {.f1=sin}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"sinh", {.f1=sinh}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"sqrt", {.f1=sqrt}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"tan", {.f1=tan}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"tanh", {.f1=tanh}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {0, {0}, 0, 0} -}; - -static const te_variable *find_builtin(const char *name, size_t len) { - int imin = 0; - int imax = sizeof(functions) / sizeof(te_variable) - 2; - - /*Binary search.*/ - while (imax >= imin) { - const int i = (imin + ((imax-imin)/2)); - int c = strncmp(name, functions[i].name, len); - if (!c) c = '\0' - functions[i].name[len]; - if (c == 0) { - return functions + i; - } else if (c > 0) { - imin = i + 1; - } else { - imax = i - 1; - } - } - - return 0; -} - -static const te_variable *find_lookup(const state *s, const char *name, size_t len) { - int iters; - const te_variable *var; - if (!s->lookup) return 0; - - for (var = s->lookup, iters = s->lookup_len; iters; ++var, --iters) { - if (strncmp(name, var->name, len) == 0 && var->name[len] == '\0') { - return var; - } - } - return 0; -} - - - -static double add(double a, double b) {return a + b;} -static double sub(double a, double b) {return a - b;} -static double mul(double a, double b) {return a * b;} -static double divide(double a, double b) {return a / b;} -static double negate(double a) {return -a;} -static double comma(double a, double b) {(void)a; return b;} -static double is_gt(double a, double b) { return a > b ? 1 : 0; } -static double is_ge(double a, double b) { return a >= b ? 1 : 0; } -static double is_lt(double a, double b) { return a < b ? 1 : 0; } -static double is_le(double a, double b) { return a <= b ? 1 : 0; } -static double is_eq(double a, double b) { return a == b ? 1 : 0; } -static double is_neq(double a, double b) { return a != b ? 1 : 0; } - -static int next_if(state *s, const char c) { - int r = *s->next && s->next[0] == c; - if (r) s->next++; - return r; -} - -static void next_token(state *s) { - s->type = TOK_NULL; - - do { - - if (!*s->next){ - s->type = TOK_END; - return; - } - - /* Try reading a number. */ - if ((s->next[0] >= '0' && s->next[0] <= '9') || s->next[0] == '.') { - s->v.value = strtod(s->next, (char**)&s->next); - s->type = TOK_NUMBER; - } else { - /* Look for a variable or builtin function call. */ - if (s->next[0] >= 'a' && s->next[0] <= 'z') { - const char *start; - start = s->next; - while ((s->next[0] >= 'a' && s->next[0] <= 'z') || (s->next[0] >= '0' && s->next[0] <= '9') || (s->next[0] == '_')) s->next++; - - const te_variable *var = find_lookup(s, start, (size_t)(s->next - start)); - if (!var) var = find_builtin(start, (size_t)(s->next - start)); - - if (!var) { - s->type = TOK_ERROR; - } else { - switch(TYPE_MASK(var->type)) - { - case TE_VARIABLE: - s->type = TOK_VARIABLE; - s->v.bound = var->v.bound; - break; - - case TE_OFFSET: - s->type = TOK_OFFSET; - s->v.offset = var->v.offset; - break; - - case TE_CONSTANT: - s->type = TE_CONSTANT; - s->v.value = var->v.value; - break; - -#ifndef TE_WITHOUT_CLOSURES - case TE_CLOSURE0: case TE_CLOSURE1: case TE_CLOSURE2: case TE_CLOSURE3: - case TE_CLOSURE4: case TE_CLOSURE5: case TE_CLOSURE6: case TE_CLOSURE7: - s->context = var->context; -#endif -#ifndef TE_WITHOUT_FUNCTION_0 - /* fall through */ - case TE_FUNCTION0: -#endif - /* fall through */ - case TE_FUNCTION1: case TE_FUNCTION2: case TE_FUNCTION3: -#if TE_MAX_FUNCTION_ARITY >= 4 - /* fall through */ - case TE_FUNCTION4: -#endif -#if TE_MAX_FUNCTION_ARITY >= 5 - /* fall through */ - case TE_FUNCTION5: -#endif -#if TE_MAX_FUNCTION_ARITY >= 6 - /* fall through */ - case TE_FUNCTION6: -#endif -#if TE_MAX_FUNCTION_ARITY == 7 - /* fall through */ - case TE_FUNCTION7: -#endif - s->type = var->type; - s->v.any = var->v.any; - break; - } - } - - } else { - /* Look for an operator or special character. */ - switch (s->next++[0]) { - case '+': s->type = TOK_INFIX; s->v.f2 = add; break; - case '-': s->type = TOK_INFIX; s->v.f2 = sub; break; - case '*': s->type = TOK_INFIX; s->v.f2 = mul; break; - case '/': s->type = TOK_INFIX; s->v.f2 = divide; break; - case '^': s->type = TOK_INFIX; s->v.f2 = pow; break; - case '%': s->type = TOK_INFIX; s->v.f2 = fmod; break; - case '>': s->type = TOK_INFIX; - s->v.f2 = next_if(s, '=') ? is_ge : is_gt; break; - case '<': s->type = TOK_INFIX; - s->v.f2 = next_if(s, '=') ? is_le : is_lt; break; - case '=': s->type = TOK_INFIX; - /* The only valid char after = is = */ - if (next_if(s, '=')) s->v.f2 = is_eq; else s->type = TOK_ERROR; - break; - case '!': s->type = TOK_INFIX; - /* The only valid char after ! is = */ - if (next_if(s, '=')) s->v.f2 = is_neq; else s->type = TOK_ERROR; - break; - case '(': s->type = TOK_OPEN; break; - case ')': s->type = TOK_CLOSE; break; - case ',': s->type = TOK_SEP; break; - case ' ': case '\t': case '\n': case '\r': break; - default: s->type = TOK_ERROR; break; - } - } - } - } while (s->type == TOK_NULL); -} - - -static te_expr *list(state *s); -static te_expr *eql(state *s); -static te_expr *power(state *s); - -static te_expr *base(state *s) { - /* <base> = <constant> | <variable> | <function-0> {"(" ")"} | <function-1> <power> | <function-X> "(" <expr> {"," <expr>} ")" | "(" <list> ")" */ - te_expr *ret; - int arity; - - switch (TYPE_MASK(s->type)) { - case TOK_NUMBER: - ret = new_expr(TE_CONSTANT, 0); - ret->v.value = s->v.value; - next_token(s); - break; - - case TOK_VARIABLE: - ret = new_expr(TE_VARIABLE, 0); - ret->v.bound = s->v.bound; - next_token(s); - break; - - case TOK_OFFSET: - ret = new_expr(TE_OFFSET, 0); - ret->v.offset = s->v.offset; - next_token(s); - break; - - case TE_CONSTANT: - ret = new_expr(TE_CONSTANT, 0); - ret->v.value = s->v.value; - next_token(s); - break; - -#if ! defined(TE_WITHOUT_FUNCTION_0) || ! defined(TE_WITHOUT_CLOSURES) -#ifndef TE_WITHOUT_FUNCTION_0 - case TE_FUNCTION0: -#endif -#ifndef TE_WITHOUT_CLOSURES - case TE_CLOSURE0: -#endif - ret = new_expr(s->type, 0); - ret->v.any = s->v.any; -#ifndef TE_WITHOUT_CLOSURES - if (IS_CLOSURE(s->type)) ret->parameters[0] = s->context; -#endif - next_token(s); - if (s->type == TOK_OPEN) { - next_token(s); - if (s->type != TOK_CLOSE) { - s->type = TOK_ERROR; - } else { - next_token(s); - } - } - break; -#endif - - case TE_FUNCTION1: -#ifndef TE_WITHOUT_CLOSURES - case TE_CLOSURE1: -#endif - ret = new_expr(s->type, 0); - ret->v.any = s->v.any; -#ifndef TE_WITHOUT_CLOSURES - if (IS_CLOSURE(s->type)) ret->parameters[1] = s->context; -#endif - next_token(s); - ret->parameters[0] = power(s); - break; - - case TE_FUNCTION2: case TE_FUNCTION3: -#if TE_MAX_FUNCTION_ARITY >= 4 - case TE_FUNCTION4: -#endif -#if TE_MAX_FUNCTION_ARITY >= 5 - case TE_FUNCTION5: -#endif -#if TE_MAX_FUNCTION_ARITY >= 6 - case TE_FUNCTION6: -#endif -#if TE_MAX_FUNCTION_ARITY == 7 - case TE_FUNCTION7: -#endif -#ifndef TE_WITHOUT_CLOSURES - case TE_CLOSURE2: case TE_CLOSURE3: case TE_CLOSURE4: - case TE_CLOSURE5: case TE_CLOSURE6: case TE_CLOSURE7: -#endif - arity = ARITY(s->type); - - ret = new_expr(s->type, 0); - ret->v.any = s->v.any; -#ifndef TE_WITHOUT_CLOSURES - if (IS_CLOSURE(s->type)) ret->parameters[arity] = s->context; -#endif - next_token(s); - - if (s->type != TOK_OPEN) { - s->type = TOK_ERROR; - } else { - int i; - for(i = 0; i < arity; i++) { - next_token(s); - ret->parameters[i] = eql(s); - if(s->type != TOK_SEP) { - break; - } - } - if(s->type != TOK_CLOSE || i != arity - 1) { - s->type = TOK_ERROR; - } else { - next_token(s); - } - } - - break; - - case TOK_OPEN: - next_token(s); - ret = list(s); - if (s->type != TOK_CLOSE) { - s->type = TOK_ERROR; - } else { - next_token(s); - } - break; - - default: - ret = new_expr(0, 0); - s->type = TOK_ERROR; - ret->v.value = NAN; - break; - } - - return ret; -} - - -static te_expr *power(state *s) { - /* <power> = {("-" | "+")} <base> */ - int sign = 1; - while (s->type == TOK_INFIX && (s->v.f2 == add || s->v.f2 == sub)) { - if (s->v.f2 == sub) sign = -sign; - next_token(s); - } - - te_expr *ret; - - if (sign == 1) { - ret = base(s); - } else { - ret = new_expr1(TE_FUNCTION1 | TE_FLAG_PURE, base(s)); - ret->v.f1 = negate; - } - - return ret; -} - -#ifdef TE_POW_FROM_RIGHT -static te_expr *factor(state *s) { - /* <factor> = <power> {"^" <power>} */ - te_expr *ret = power(s); - - int neg = 0; - te_expr *insertion = 0; - - if (ret->type == (TE_FUNCTION1 | TE_FLAG_PURE) && ret->v.f1 == negate) { - te_expr *se = ret->parameters[0]; - free(ret); - ret = se; - neg = 1; - } - - while (s->type == TOK_INFIX && (s->v.f2 == pow)) { - te_fun2 t = s->v.f2; - next_token(s); - - if (insertion) { - /* Make exponentiation go right-to-left. */ - te_expr *insert = new_expr2(TE_FUNCTION2 | TE_FLAG_PURE, insertion->parameters[1], power(s)); - insert->v.f2 = t; - insertion->parameters[1] = insert; - insertion = insert; - } else { - ret = new_expr2(TE_FUNCTION2 | TE_FLAG_PURE, ret, power(s)); - ret->v.f2 = t; - insertion = ret; - } - } - - if (neg) { - ret = new_expr1(TE_FUNCTION1 | TE_FLAG_PURE, ret); - ret->v.f1 = negate; - } - - return ret; -} -#else -static te_expr *factor(state *s) { - /* <factor> = <power> {"^" <power>} */ - te_expr *ret = power(s); - - while (s->type == TOK_INFIX && (s->v.f2 == pow)) { - te_fun2 t = s->v.f2; - next_token(s); - ret = new_expr2(TE_FUNCTION2 | TE_FLAG_PURE, ret, power(s)); - ret->v.f2 = t; - } - - return ret; -} -#endif - - - -static te_expr *term(state *s) { - /* <term> = <factor> {("*" | "/" | "%") <factor>} */ - te_expr *ret = factor(s); - - while (s->type == TOK_INFIX && (s->v.f2 == mul || s->v.f2 == divide || s->v.f2 == fmod)) { - te_fun2 t = s->v.f2; - next_token(s); - ret = new_expr2(TE_FUNCTION2 | TE_FLAG_PURE, ret, factor(s)); - ret->v.f2 = t; - } - - return ret; -} - - -static te_expr *expr(state *s) { - /* <expr> = <term> {("+" | "-") <term>} */ - te_expr *ret = term(s); - - while (s->type == TOK_INFIX && (s->v.f2 == add || s->v.f2 == sub)) { - te_fun2 t = s->v.f2; - next_token(s); - ret = new_expr2(TE_FUNCTION2 | TE_FLAG_PURE, ret, term(s)); - ret->v.f2 = t; - } - - return ret; -} - - -static te_expr *cmp(state *s) { - /* <cmp> = <expr> {(">" | "<" | ">=" | "<=") <expr>} */ - te_expr *ret = expr(s); - - while (s->type == TOK_INFIX - && (s->v.f2 == is_lt || s->v.f2 == is_le || s->v.f2 == is_gt || s->v.f2 == is_ge)) { - te_fun2 t = s->v.f2; - next_token(s); - ret = new_expr2(TE_FUNCTION2 | TE_FLAG_PURE, ret, expr(s)); - ret->v.f2 = t; - } - - return ret; -} - - -static te_expr *eql(state *s) { - /* <eql> = <cmp> {("==" | "!=") <cmp>} */ - te_expr *ret = cmp(s); - - while (s->type == TOK_INFIX && (s->v.f2 == is_eq || s->v.f2 == is_neq)) { - te_fun2 t = s->v.f2; - next_token(s); - ret = new_expr2(TE_FUNCTION2 | TE_FLAG_PURE, ret, cmp(s)); - ret->v.f2 = t; - } - - return ret; -} - - -static te_expr *list(state *s) { - /* <list> = <eql> {"," <eql>} */ - te_expr *ret = eql(s); - - while (s->type == TOK_SEP) { - next_token(s); - ret = new_expr2(TE_FUNCTION2 | TE_FLAG_PURE, ret, eql(s)); - ret->v.f2 = comma; - } - - return ret; -} - - -#define M(e) te_eval(n->parameters[e], base_addr) - - -double te_eval(const te_expr *n, const void* base_addr) { - if (!n) return NAN; - - switch(TYPE_MASK(n->type)) { - case TE_CONSTANT: return n->v.value; - case TE_VARIABLE: return *n->v.bound; - case TE_OFFSET: assert(base_addr); - return *(double*)(((char*)base_addr)+n->v.offset); - -#ifndef TE_WITHOUT_FUNCTION_0 - case TE_FUNCTION0: return n->v.f0(); -#endif - case TE_FUNCTION1: return n->v.f1(M(0)); - case TE_FUNCTION2: return n->v.f2(M(0), M(1)); - case TE_FUNCTION3: - if (IS_CONDITION(n->type)) return M(0) ? M(1) : M(2); - else return n->v.f3(M(0), M(1), M(2)); -#if TE_MAX_FUNCTION_ARITY >= 4 - case TE_FUNCTION4: return n->v.f4(M(0), M(1), M(2), M(3)); -#endif -#if TE_MAX_FUNCTION_ARITY >= 5 - case TE_FUNCTION5: return n->v.f5(M(0), M(1), M(2), M(3), M(4)); -#endif -#if TE_MAX_FUNCTION_ARITY >= 6 - case TE_FUNCTION6: return n->v.f6(M(0), M(1), M(2), M(3), M(4), M(5)); -#endif -#if TE_MAX_FUNCTION_ARITY == 7 - case TE_FUNCTION7: return n->v.f7(M(0), M(1), M(2), M(3), M(4), M(5), M(6)); -#endif - -#ifndef TE_WITHOUT_CLOSURES - case TE_CLOSURE0: return n->v.cl0(n->parameters[0]); - case TE_CLOSURE1: return n->v.cl1(n->parameters[1], M(0)); - case TE_CLOSURE2: return n->v.cl2(n->parameters[2], M(0), M(1)); - case TE_CLOSURE3: return n->v.cl3(n->parameters[3], M(0), M(1), M(2)); - case TE_CLOSURE4: return n->v.cl4(n->parameters[4], M(0), M(1), M(2), M(3)); - case TE_CLOSURE5: return n->v.cl5(n->parameters[5], M(0), M(1), M(2), M(3), M(4)); - case TE_CLOSURE6: return n->v.cl6(n->parameters[6], M(0), M(1), M(2), M(3), M(4), M(5)); - case TE_CLOSURE7: return n->v.cl7(n->parameters[7], M(0), M(1), M(2), M(3), M(4), M(5), M(6)); -#endif - - default: return NAN; - } - -} - -#undef M - -static int expr_equal(const te_expr* e1, const te_expr* e2) { - int i; - if (e1->type != e2->type) return 0; - if (e1->v.any != e2->v.any) return 0; - for (i = 0; i < ARITY(e1->type); i++) { - if(!expr_equal(e1->parameters[i], e2->parameters[i])) return 0; - } - return 1; -} - -static void optimize(te_expr *n) { - /* Only optimize out conditions and functions flagged as pure. */ - if (IS_CONDITION(n->type)) { - te_expr* cond = n->parameters[0]; - assert(IS_FUNCTION(n->type) && ARITY(n->type) == 3); - optimize(cond); - if (cond->type == TE_CONSTANT) { - te_expr* keep = (cond->v.value) ? n->parameters[1] : n->parameters[2]; - /* Can keep either param[1] or param[2] */ - optimize(keep); - n->type = keep->type; - n->v.any = keep->v.any; - te_free_parameters(n); - } - else { /* c ? x : x is x */ - te_expr* if_branch = n->parameters[1]; - te_expr* else_branch = n->parameters[2]; - optimize(if_branch); - optimize(else_branch); - if (expr_equal(if_branch, else_branch)) { - n->type = if_branch->type; - n->v.any = if_branch->v.any; - te_free_parameters(n); - } - } - } - else if (IS_PURE(n->type)) { - const int arity = ARITY(n->type); - int known = 1; - int i; - assert(IS_FUNCTION(n->type)); - for (i = 0; i < arity; ++i) { - optimize(n->parameters[i]); - if (((te_expr*)(n->parameters[i]))->type != TE_CONSTANT) { - known = 0; - } - } - if (known) { - const double value = te_eval(n, NULL); - te_free_parameters(n); - n->type = TE_CONSTANT; - n->v.value = value; - } - } -} - - -te_expr *te_compile(const char *expression, const te_variable *variables, int var_count, int *error) { - state s; - s.start = s.next = expression; - s.lookup = variables; - s.lookup_len = var_count; - - next_token(&s); - te_expr *root = list(&s); - - if (s.type != TOK_END) { - te_free(root); - if (error) { - *error = (int)(s.next - s.start); - if (*error == 0) *error = 1; - } - return 0; - } else { - optimize(root); - if (error) *error = 0; - return root; - } -} - - -double te_interp(const char *expression, int *error) { - te_expr *n = te_compile(expression, 0, 0, error); - double ret; - if (n) { - ret = te_eval(n, NULL); - te_free(n); - } else { - ret = NAN; - } - return ret; -} - -static void pn (const te_expr *n, int depth) { - int i, arity; - printf("%*s", depth, ""); - - switch(TYPE_MASK(n->type)) { - case TE_CONSTANT: printf("%f\n", n->v.value); break; - case TE_VARIABLE: printf("bound %p\n", (void*) n->v.bound); break; - case TE_OFFSET: printf("offset %zu\n", n->v.offset); break; - -#ifndef TE_WITHOUT_FUNCTION_0 - case TE_FUNCTION0: -#endif - case TE_FUNCTION1: case TE_FUNCTION2: case TE_FUNCTION3: -#if TE_MAX_FUNCTION_ARITY >= 4 - case TE_FUNCTION4: -#endif -#if TE_MAX_FUNCTION_ARITY >= 5 - case TE_FUNCTION5: -#endif -#if TE_MAX_FUNCTION_ARITY >= 6 - case TE_FUNCTION6: -#endif -#if TE_MAX_FUNCTION_ARITY == 7 - case TE_FUNCTION7: -#endif -#ifndef TE_WITHOUT_CLOSURES - case TE_CLOSURE0: case TE_CLOSURE1: case TE_CLOSURE2: case TE_CLOSURE3: - case TE_CLOSURE4: case TE_CLOSURE5: case TE_CLOSURE6: case TE_CLOSURE7: -#endif - arity = ARITY(n->type); - printf("f%d", arity); - for(i = 0; i < arity; i++) { - printf(" %p", n->parameters[i]); - } - printf("\n"); - for(i = 0; i < arity; i++) { - pn(n->parameters[i], depth + 1); - } - break; - } -} - - -void te_print(const te_expr *n) { - pn(n, 0); -} diff --git a/tinyexpr/tinyexpr.h b/tinyexpr/tinyexpr.h @@ -1,197 +0,0 @@ -/* - * TINYEXPR - Tiny recursive descent parser and evaluation engine in C - * - * Copyright (c) 2015-2018 Lewis Van Winkle - * - * http://CodePlea.com - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgement in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#ifndef __TINYEXPR_H__ -#define __TINYEXPR_H__ - -#include <stddef.h> - -/* Helper macro to be used when defining a te_variable */ -#define TE_DEF_VARIABLE(Name, Var) {(Name), {.var=&(Var)}, TE_VARIABLE, NULL} -#define TE_DEF_OFFSET(Name, Offset) {(Name), {.offset=(Offset)}, TE_OFFSET, NULL} -#define TE_DEF_CONSTANT(Name, Value) {(Name), {.value=(Value)}, TE_CONSTANT, NULL} -#define TE_DEF_FUNCTION(Name, Fun, Arity) {(Name), {.f##Arity=(Fun)}, TE_FUNCTION##Arity, NULL} -#ifndef TE_WITHOUT_CLOSURES -#define TE_DEF_CLOSURE(Name, Closure, Arity, Ctx) {(Name), {.cl##Arity=(Closure)}, TE_CLOSURE##Arity, (Ctx)} -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef TE_MAX_FUNCTION_ARITY -#if !(TE_MAX_FUNCTION_ARITY >= 3 && TE_MAX_FUNCTION_ARITY <= 7) -#error Valid range for TE_MAX_FUNCTION_ARITY is [3 7] -#endif -#else -#define TE_MAX_FUNCTION_ARITY 7 -#endif - - -#ifndef TE_WITHOUT_FUNCTION_0 -#define FUN_TYPE_0 \ - double(*f0)(void); -#else -#define FUN_TYPE_0 -#endif -#define FUN_TYPES_1_3 \ - void *any;\ - double(*f1)(double);\ - double(*f2)(double, double);\ - double(*f3)(double, double, double); -#if TE_MAX_FUNCTION_ARITY >= 4 -#define FUN_TYPE_4 \ - double(*f4)(double, double, double, double); -#else -#define FUN_TYPE_4 -#endif -#if TE_MAX_FUNCTION_ARITY >= 5 -#define FUN_TYPE_5 \ - double(*f5)(double, double, double, double, double); -#else -#define FUN_TYPE_5 -#endif -#if TE_MAX_FUNCTION_ARITY >= 6 -#define FUN_TYPE_6 \ - double(*f6)(double, double, double, double, double, double); -#else -#define FUN_TYPE_6 -#endif -#if TE_MAX_FUNCTION_ARITY == 7 -#define FUN_TYPE_7 \ - double(*f7)(double, double, double, double, double, double, double); -#else -#define FUN_TYPE_7 -#endif - -#ifndef TE_WITHOUT_CLOSURES -#define CLOSURE_TYPES \ - double(*cl0)(void*);\ - double(*cl1)(void*, double);\ - double(*cl2)(void*, double, double);\ - double(*cl3)(void*, double, double, double);\ - double(*cl4)(void*, double, double, double, double);\ - double(*cl5)(void*, double, double, double, double, double);\ - double(*cl6)(void*, double, double, double, double, double, double);\ - double(*cl7)(void*, double, double, double, double, double, double, double); -#else -#define CLOSURE_TYPES -#endif - -union fun { - FUN_TYPE_0 - FUN_TYPES_1_3 - FUN_TYPE_4 - FUN_TYPE_5 - FUN_TYPE_6 - FUN_TYPE_7 - CLOSURE_TYPES -}; - -union value { - FUN_TYPE_0 - FUN_TYPES_1_3 - FUN_TYPE_4 - FUN_TYPE_5 - FUN_TYPE_6 - FUN_TYPE_7 - CLOSURE_TYPES - double value; - const double *var; - size_t offset; - const double *bound; -}; - -typedef struct te_expr { - int type; - union value v; - void *parameters[1]; -} te_expr; - - -enum { - TE_VARIABLE = 0, - TE_OFFSET = 1, - TE_CONSTANT = 2, - -#ifndef TE_WITHOUT_FUNCTION_0 - TE_FUNCTION0 = 8, -#endif - TE_FUNCTION1 = 9, TE_FUNCTION2, TE_FUNCTION3, -#if TE_MAX_FUNCTION_ARITY >= 4 - TE_FUNCTION4, -#endif -#if TE_MAX_FUNCTION_ARITY >= 5 - TE_FUNCTION5, -#endif -#if TE_MAX_FUNCTION_ARITY >= 6 - TE_FUNCTION6, -#endif -#if TE_MAX_FUNCTION_ARITY == 7 - TE_FUNCTION7, -#endif -#ifndef TE_WITHOUT_CLOSURES - TE_CLOSURE0 = 16, TE_CLOSURE1, TE_CLOSURE2, TE_CLOSURE3, - TE_CLOSURE4, TE_CLOSURE5, TE_CLOSURE6, TE_CLOSURE7, -#endif - TE_FLAG_PURE = 64 -}; - -typedef struct te_variable { - const char *name; - const union value v; - int type; - /* context could be member only if TE_WITHOUT_CLOSURES is undefined - * we don't take the opportunity as it would add too much complexity */ - void *context; -} te_variable; - - - -/* Parses the input expression, evaluates it, and frees it. */ -/* Returns NaN on error. */ -double te_interp(const char *expression, int *error); - -/* Parses the input expression and binds variables. */ -/* Returns NULL on error. */ -te_expr *te_compile(const char *expression, const te_variable *variables, int var_count, int *error); - -/* Evaluates the expression. */ -double te_eval(const te_expr *n, const void* base_addr); - -/* Prints debugging information on the syntax tree. */ -void te_print(const te_expr *n); - -/* Frees the expression. */ -/* This is safe to call on NULL pointers. */ -void te_free(te_expr *n); - -/* Duplicates a te_expr */ -te_expr* te_duplicate(te_expr* n); - -#ifdef __cplusplus -} -#endif - -#endif /*__TINYEXPR_H__*/