commit 8d0faf6bb9b35af8e7d8db3b33ef2f07d551c770
parent c67fd6f865f9d91dcf12e042c5dfa9c1c53d4ea3
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date: Mon, 7 Sep 2026 16:12:38 +0200
Merge branch 'release_0.5'
Diffstat:
| M | Makefile | | | 46 | ++++++++++++++++++++++++++++++++++++++-------- |
| M | README.md | | | 60 | ++++++++++++++++++++++++++++++++++++++---------------------- |
| M | config.mk | | | 50 | +++++++++++++++++++++++++++++++++++++++++++++++--- |
| M | cyl/Makefile | | | 9 | ++++++--- |
| A | doc/solmaps.1 | | | 111 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | doc/solpaths.1 | | | 103 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | doc/solpp.1 | | | 142 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | doc/solppraw.1 | | | 96 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| D | solmaps.c | | | 75 | --------------------------------------------------------------------------- |
| D | solpaths.c | | | 49 | ------------------------------------------------- |
| D | solpp.c | | | 338 | ------------------------------------------------------------------------------- |
| D | solpp.h | | | 450 | ------------------------------------------------------------------------------- |
| D | solppraw.c | | | 165 | ------------------------------------------------------------------------------- |
| A | src/solmaps.c | | | 92 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/solpaths.c | | | 58 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/solpp.c | | | 352 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/solpp.h | | | 470 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/solppraw.c | | | 182 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | themis/Makefile | | | 11 | +++++++---- |
| M | themis/themis.c | | | 1 | + |
20 files changed, 1743 insertions(+), 1117 deletions(-)
diff --git a/Makefile b/Makefile
@@ -17,26 +17,56 @@
include config.mk
+PROG =\
+ solmaps \
+ solpp \
+ solppraw \
+ solpaths
-SRC = solmaps.c solpp.c solppraw.c solpaths.c
-PROG = $(SRC:.c=)
+MAN = \
+ doc/solmaps.1 \
+ doc/solpp.1 \
+ doc/solpaths.1 \
+ doc/solppraw.1
PROJECTS = themis cyl
default: $(PROG)
-install: $(PROG)
- mkdir -p $(DESTDIR)$(PREFIX)/bin
- cp $(PROG) $(DESTDIR)$(PREFIX)/bin
- for i in $(PROG); do chmod 755 "$(DESTDIR)$(PREFIX)/bin/$${i}"; done
+install: $(PROG) $(MAN)
+ install() { mode="$$1"; prefix="$$2"; shift 2; \
+ mkdir -p "$${prefix}"; \
+ cp "$$@" "$${prefix}"; \
+ chmod "$${mode}" "$${prefix}/$${@##*/}"; \
+ }; \
+ install 644 "$(DESTDIR)$(PREFIX)/share/doc/solstice-pp" COPYING; \
+ install 644 "$(DESTDIR)$(PREFIX)/share/doc/solstice-pp" README.md; \
+ for i in $(PROG); \
+ do install 755 "$(DESTDIR)$(PREFIX)/bin" $${i}; done; \
+ for i in $(MAN); \
+ do install 644 "$(DESTDIR)$(PREFIX)/share/man/man1" $${i}; done;
+
+uninstall:
+ rm -f "$(DESTDIR)$(PREFIX)/share/doc/solstice-pp/COPYING"
+ rm -f "$(DESTDIR)$(PREFIX)/share/doc/solstice-pp/README.md"
+ for i in $(PROG); do rm -f "$(DESTDIR)$(PREFIX)/bin/$${i}"; done
+ for i in $(MAN); do rm -f "$(DESTDIR)$(PREFIX)/share/man/man1/$${i##*/}"; done
.PHONY: clean
clean:
rm -rf $(PROG)
for i in $(PROJECTS); do $(MAKE) -C "$${i}" clean; done
-$(PROG): solpp.h
- $(CC) -o $@ $(CFLAGS) $@.c
+solmaps: src/solmaps.c
+
+solpp: src/solpp.c
+
+solppraw: src/solppraw.c
+
+solpaths: src/solpaths.c
+
+$(PROG): src/solpp.h config.mk
+ $(CC) -o $@ $(CFLAGS) src/$@.c
run: $(PROJECTS)
diff --git a/README.md b/README.md
@@ -1,40 +1,43 @@
# Solstice Post-Process
-Solstice resuts post-processing tools.
-In addition, the code base contains programs that generate input files
-for systems to be simulated by Solstice.
-
-The sources of the post-processing tools are located at the root of the
-repository.
-Each sub-directory contains the sources of the programs used to generate
-the Solstice input files for a specific solar power plant.
-
-All these programs are written in standard C, with no external
-dependencies.
-They can therefore be compiled by any C compiler that supports the C99
-standard.
-However, to simplify the compilation process on POSIX-compatible
-systems, POSIX Makefiles are supplied.
-These are used not only to build the programs, but also to run solstice
-simulations on the generated input files and to post-process their
-results.
+Post-processing tools for Solstice outputs and associated tests.
+
+The sources of the post-processing tools are located in the src directory.
+The `themis' test is dynamically generated by building and running a dedicated
+code, whose source code is locates in the `themis' subdirectory.
+The `cyl' test, that is simple, is run from provided input files.
+
+All this code is written in C99, with no external dependencies.
+It can therefore be compiled by any C compiler that supports the C99 standard.
+To simplify compiling on POSIX-compatible systems, POSIX Makefiles are provided.
+They can be used not only to build the tools, but also to run solstice tests
+and to post-process their results.
## Requirements
- C compiler (C99)
- POSIX make
-- Solstice (optional for test cases)
+- Solstice (to run the tests)
+
+## How to build
-## Installation
+These programs, as part of the Solstice app suite, can be built on any x86-64
+POSIX system.
-Edit config.mk as needed, then run:
+Note that you will most likely want to build the entire Solstice suite rather
+than these programs alone. If so, a good starting point is the dedicated
+[Solstice web page](https://www.meso-star.com/solstice/install.html).
+
+First ensure that the make utility and a C99 compiler are installed on your
+system. Then, edit the config.mk file to meet your needs and build the project
+by running:
make clean install
## Quick start
Two examples are provided in the `cyl` and `themis` directories.
-Each contain a Makefile that automate a complete workflow, from scene
+Each contain a Makefile that automates a complete workflow, from scene
generation, to post-processings with the provided tools, passing to
Solstice computation.
@@ -44,6 +47,19 @@ To run the test cases :
## Release notes
+### Version 0.5
+
+- Cope with requirements of solstice 0.11, starting with new output format
+ items.
+
+- Fix solpp and solppraw crash when reading on stdin.
+
+- Add man pages.
+
+### Version 0.4
+
+- Replace CMake with a POSIX Makefile.
+
### Version 0.3.1
- Fix VTK files generated by `solpp`: data were written as double while
diff --git a/config.mk b/config.mk
@@ -13,8 +13,52 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-VERSION = 0.3.1
+VERSION_MAJOR = 0
+VERSION_MINOR = 5
+VERSION_PATCH = 0
+VERSION = $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH)
+
PREFIX = /usr/local
-CFLAGS = -O2 -std=c99 -pedantic -Wall
-#CFLAGS = -g -std=c99 -pedantic -Wall
+BUILD_TYPE = RELEASE
+#BUILD_TYPE = DEBUG
+
+################################################################################
+# Tools
+################################################################################
+AR = ar
+CC = cc
+LD = ld
+OBJCOPY = objcopy
+PKG_CONFIG = pkg-config
+RANLIB = ranlib
+
+################################################################################
+# Compilation options
+################################################################################
+WFLAGS =\
+ -Wall\
+ -Wcast-align\
+ -Wconversion\
+ -Wextra\
+ -Wmissing-declarations\
+ -Wmissing-prototypes\
+ -Wshadow
+
+CFLAGS_HARDENED =\
+ -D_FORTIFY_SOURCES=2\
+ -fcf-protection=full\
+ -fstack-clash-protection\
+ -fstack-protector-strong
+
+CFLAGS_COMMON =\
+ -std=c99\
+ -pedantic\
+ -fvisibility=hidden\
+ -fstrict-aliasing\
+ $(CFLAGS_HARDENED)\
+ $(WFLAGS)
+
+CFLAGS_RELEASE = -O2 -DNDEBUG $(CFLAGS_COMMON)
+CFLAGS_DEBUG = -g $(CFLAGS_COMMON)
+CFLAGS = $(CFLAGS_$(BUILD_TYPE))
diff --git a/cyl/Makefile b/cyl/Makefile
@@ -29,13 +29,16 @@ SIMUL = simul
default: run
simul: $(INPUT)
- solstice -D$(SUN_DIRS) -n$(NEXPERIMENTS) -R$(RCV) -fo $(SIMUL) $(INPUT)
- solstice -D$(SUN_DIRS) -g format=obj:split=geometry -fo $(GEOM) $(INPUT)
+ solstice $$(for d in $(SUN_DIRS); do echo -f "-D$${d} "; done) \
+ -n$(NEXPERIMENTS) -R$(RCV) -fo $(SIMUL) $(INPUT)
+ solstice $$(for d in $(SUN_DIRS); do echo -f "-D$${d} "; done) \
+ -g format=obj:split=geometry -fo $(GEOM) $(INPUT)
../solppraw $(SIMUL)
../solpp $(GEOM) $(SIMUL)
paths: $(PROG) $(INPUT)
- solstice -D$(SUN_DIRS) -q -n$(NPATHS) -R$(RCV) -p default $(INPUT) | ../solpaths
+ solstice $$(for d in $(SUN_DIRS); do echo -f "-D$${d} "; done) \
+ -q -n$(NPATHS) -R$(RCV) -p default $(INPUT) | ../solpaths
run: paths simul
diff --git a/doc/solmaps.1 b/doc/solmaps.1
@@ -0,0 +1,111 @@
+.\" SPDX-License-Identifier: GPL-3.0-or-later
+.\" Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com)
+.\"
+.\" This is free documentation: 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 manual 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/>.
+.Dd $Mdocdate$
+.Dt SOLMAPS 1
+.Os
+.Sh NAME
+.Nm solmaps
+.Nd extract and save receiver flux maps from Solstice results
+.Sh SYNOPSIS
+.Nm
+.Op Ar file
+.Sh DESCRIPTION
+.Nm
+reads the results of a
+.Xr solstice 1
+simulation from
+.Ar file
+or from standard input whether a filename is provided as an argument or not,
+respectively.
+It extracts the maps of incoming flux computed by
+.Xr solstice 1
+for the receivers whose
+.Cm per_primitive
+flag is enabled.
+Refer to
+.Xr solstice-receiver 5
+for more information.
+Each map is then saved in a separate
+.Tn VTK
+file that can be visualised and analysed in a data
+visualisation tool such as
+.Lk https://www.paraview.org ParaView .
+.Pp
+The naming scheme for the output files depends on the way the sun direction was
+provided to
+.Xr solstice 1 :
+.Bl -dash
+.It
+using the
+.Fl D Ar azimuth,elevation
+flag: the output file is named after the following pattern:
+.Bd -literal -offset indent
+printf "%g-%g-%s.vtk" azimuth elevation receiver_name
+.Ed
+.It
+using the
+.Fl L Ar latitude,longitude
+and
+.Fl T Ar time
+flags: the output file is named after the following pattern:
+.Bd -literal -offset indent
+printf "%g-%g-%s-%s.vtk" latitude longitude time receiver_name
+.Ed
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+Pipe
+.Xr solstice 1
+with
+.Nm
+to extract the flux maps of the receivers for each provided sun direction:
+.Bd -literal -offset indent
+solstice -D45,70 -D50,75 -R rcvs.yaml input.yaml | solmaps
+.Ed
+.Pp
+First, invoke
+.Xr solstice 1
+to write the results to
+.Pa output ,
+then run
+.Nm
+on this file to extract the flux maps of the receivers for the provided sun
+direction:
+.Bd -literal -offset indent
+solstice -L3,40 -T"2026-08-15T17:20:00" -R rcvs.yaml -o output input.yaml
+solmaps output
+.Ed
+.Sh SEE ALSO
+.Xr solpaths 1 ,
+.Xr solpp 1 ,
+.Xr solppraw 1 ,
+.Xr solstice 1 ,
+.Xr solstice-output 5 ,
+.Xr solstice-receiver 5
+.Sh HISTORY
+.Nm
+was initially developed with funding from the
+.Em SOLSTICE LabEx
+.Pq Laboratory of Excellence ,
+in collaboration with the PROMES Laboratory of the
+French National Centre for Scientific Research
+.Pq CNRS .
+Starting in 2026, a new development effort funded by Ademe is ongoing.
+.Sh AUTHORS
+.Nm
+was written and is maintained by
+.An |M\['e]so|Star> Aq Mt contact@meso-star.com .
diff --git a/doc/solpaths.1 b/doc/solpaths.1
@@ -0,0 +1,103 @@
+.\" SPDX-License-Identifier: GPL-3.0-or-later
+.\" Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com)
+.\"
+.\" This is free documentation: 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 manual 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/>.
+.Dd $Mdocdate$
+.Dt SOLPATHS 1
+.Os
+.Sh NAME
+.Nm solpaths
+.Nd convert Solstice radiative paths to VTK files
+.Sh SYNOPSIS
+.Nm
+.Op Ar file
+.Sh DESCRIPTION
+.Nm
+reads the radiative paths dumped by
+.Xr solstice 1
+when invoked with the
+.Fl p
+option, from
+.Ar file
+or from standard input whether a filename is provided as an argument or not,
+respectively.
+For each simulated sun direction, it generates a
+.Tn VTK
+file of the radiative paths, that can be visualised and analysed in a data
+visualisation tool such as
+.Lk https://www.paraview.org ParaView .
+.Pp
+The naming scheme for the output files depends on the way the sun direction was
+provided to
+.Xr solstice 1 :
+.Bl -dash
+.It
+using the
+.Fl D Ar azimuth,elevation
+flag: the output file is named after the following pattern:
+.Bd -literal -offset indent
+printf "%g-%g-paths.vtk" azimuth elevation
+.Ed
+.It
+using the
+.Fl L Ar latitude,longitude
+and
+.Fl T Ar time
+flags: the output file is named after the following pattern:
+.Bd -literal -offset indent
+printf "%g-%g-%s-paths.vtk" latitude longitude time
+.Ed
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+Pipe
+.Xr solstice 1
+with
+.Nm
+to extract the radiative paths for each provided sun direction:
+.Bd -literal -offset indent
+solstice -n100 -D45,70 -D50,75 -R rcvs.yaml -p default input.yaml | solpaths
+.Ed
+.Pp
+First, invoke
+.Xr solstice 1
+to write the results to
+.Pa output ,
+then run
+.Nm
+on this file to extract the radiative paths for the provided sun direction:
+.Bd -literal -offset indent
+solstice -L3,40 -T"2026-08-15T17:20:00" -R rcvs.yaml -p default -o output input.yaml
+solpaths output
+.Ed
+.Sh SEE ALSO
+.Xr solmaps 1 ,
+.Xr solpp 1 ,
+.Xr solppraw 1 ,
+.Xr solstice 1 ,
+.Xr solstice-output 5
+.Sh HISTORY
+.Nm
+was initially developed with funding from the
+.Em SOLSTICE LabEx
+.Pq Laboratory of Excellence ,
+in collaboration with the PROMES Laboratory of the
+French National Centre for Scientific Research
+.Pq CNRS .
+Starting in 2026, a new development effort funded by Ademe is ongoing.
+.Sh AUTHORS
+.Nm
+was written and is maintained by
+.An |M\['e]so|Star> Aq Mt contact@meso-star.com .
diff --git a/doc/solpp.1 b/doc/solpp.1
@@ -0,0 +1,142 @@
+.\" SPDX-License-Identifier: GPL-3.0-or-later
+.\" Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com)
+.\"
+.\" This is free documentation: 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 manual 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/>.
+.Dd $Mdocdate$
+.Dt SOLPP 1
+.Os
+.Sh NAME
+.Nm solpp
+.Nd post-process Solstice simulation results into VTK and OBJ files
+.Sh SYNOPSIS
+.Nm
+.Ar geom
+.Ar simul
+.Sh DESCRIPTION
+.Nm
+takes two input files:
+.Ar geom ,
+the geometry of a solar plant exported by
+.Xr solstice 1
+when invoked with the
+.Fl g
+option, and
+.Ar simul ,
+the results of a
+.Xr solstice 1
+simulation.
+For each simulated sun direction,
+.Nm
+writes up to three output files.
+.Pp
+The first output file is a
+.Tn VTK
+file that maps the simulation results to the meshes of the primary
+geometries, i.e.\& the reflectors.
+The mapped results include, for instance, the cosine factor of each
+reflector or the amount of flux that reaches a receiver from them.
+.Pp
+The second output file is another
+.Tn VTK
+file that stores the geometry of the receivers together with their
+simulation results, such as their incoming flux or their efficiency.
+.Pp
+The optional third output file is an
+.Tn OBJ
+file that stores the meshes of the miscellaneous geometries, i.e.\& the
+geometries that are neither receivers nor primary geometries (if any).
+.Pp
+The resulting files can be visualised and analysed in a data
+visualisation tool such as
+.Lk https://www.paraview.org ParaView .
+.Pp
+Where
+.Va end
+stands for
+.Pa primaries.vtk ,
+.Pa receivers.vtk ,
+or
+.Pa miscellaneous.obj
+for the 1st, 2nd, and optional 3rd output file respectively, the naming scheme
+for the output files depends on the way the sun direction was provided to
+.Xr solstice 1 :
+.Bl -dash
+.It
+using the
+.Fl D Ar azimuth,elevation
+flag: the output file is named after the following pattern:
+.Bd -literal -offset indent
+printf "%g-%g-%s" azimuth elevation end
+.Ed
+.It
+using the
+.Fl L Ar latitude,longitude
+and
+.Fl T Ar time
+flags: the output file is named after the following pattern:
+.Bd -literal -offset indent
+printf "%g-%g-%s-%s" latitude longitude time end
+.Ed
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+Invoke
+.Xr solstice 1
+to simulate two sun directions on the solar plant described in
+.Pa input.yaml .
+Then invoke it again to export the geometry of the solar plant for the same sun
+directions.
+Finally, post-process both outputs with
+.Nm :
+.Bd -literal -offset indent
+solstice -D45,70 -D50,75 -R rcvs.yaml -o simul input.yaml
+solstice -D45,70 -D50,75 -g format=obj -o geom input.yaml
+solpp geom simul
+.Ed
+.Pp
+Invoke
+.Xr solstice 1
+to simulate a sun direction defined by a location and a time on the solar plant
+described in
+.Pa input.yaml .
+Then invoke it again to export the geometry of the solar plant for the same sun
+direction.
+Finally, post-process both outputs with
+.Nm :
+.Bd -literal -offset indent
+solstice -L3,40 -T"2026-08-15T17:20:00" -R rcvs.yaml -o simul input.yaml
+solstice -L3,40 -T"2026-08-15T17:20:00" -g format=obj -o geom input.yaml
+solpp geom simul
+.Ed
+.Sh SEE ALSO
+.Xr solmaps 1 ,
+.Xr solpaths 1 ,
+.Xr solppraw 1 ,
+.Xr solstice 1 ,
+.Xr solstice-output 5 ,
+.Xr solstice-receiver 5
+.Sh HISTORY
+.Nm
+was initially developed with funding from the
+.Em SOLSTICE LabEx
+.Pq Laboratory of Excellence ,
+in collaboration with the PROMES Laboratory of the
+French National Centre for Scientific Research
+.Pq CNRS .
+Starting in 2026, a new development effort funded by Ademe is ongoing.
+.Sh AUTHORS
+.Nm
+was written and is maintained by
+.An |M\['e]so|Star> Aq Mt contact@meso-star.com .
diff --git a/doc/solppraw.1 b/doc/solppraw.1
@@ -0,0 +1,96 @@
+.\" SPDX-License-Identifier: GPL-3.0-or-later
+.\" Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com)
+.\"
+.\" This is free documentation: 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 manual 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/>.
+.Dd $Mdocdate$
+.Dt SOLPPRAW 1
+.Os
+.Sh NAME
+.Nm solppraw
+.Nd format raw Solstice results into human readable text
+.Sh SYNOPSIS
+.Nm
+.Op Ar file
+.Sh DESCRIPTION
+.Nm
+reads the
+.Xr solstice 1
+outputs from
+.Ar file
+or from standard input whether a filename is provided as an argument or not,
+respectively.
+For each simulated sun direction, it formats the raw results into a human
+readable text file, simplifying the analysis of the results.
+.Pp
+The naming scheme for the output files depends on the way the sun direction was
+provided to
+.Xr solstice 1 :
+.Bl -dash
+.It
+using the
+.Fl D Ar azimuth,elevation
+flag: the output file is named after the following pattern:
+.Bd -literal -offset indent
+printf "%g-%g-raw-results.txt" azimuth elevation
+.Ed
+.It
+using the
+.Fl L Ar latitude,longitude
+and
+.Fl T Ar time
+flags: the output file is named after the following pattern:
+.Bd -literal -offset indent
+printf "%g-%g-%s-results.txt" latitude longitude time
+.Ed
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+Invoke
+.Xr solstice 1
+to simulate two sun directions and pipe the results directly to
+.Nm ,
+without any intermediary file:
+.Bd -literal -offset indent
+solstice -D45,70 -D50,75 -R rcvs.yaml input.yaml | solppraw
+.Ed
+.Pp
+Alternatively, invoke
+.Xr solstice 1
+to simulate a sun direction and write the results to the
+.Pa output
+file, then post-process it with solppraw:
+.Bd -literal -offset indent
+solstice -L3,40 -T"2026-08-15T17:20:00" -R rcvs.yaml -o output input.yaml
+solppraw output
+.Ed
+.Sh SEE ALSO
+.Xr solmaps 1 ,
+.Xr solpaths 1 ,
+.Xr solpp 1 ,
+.Xr solstice 1 ,
+.Xr solstice-output 5
+.Sh HISTORY
+.Nm
+was initially developed with funding from the
+.Em SOLSTICE LabEx
+.Pq Laboratory of Excellence ,
+in collaboration with the PROMES Laboratory of the
+French National Centre for Scientific Research
+.Pq CNRS .
+Starting in 2026, a new development effort funded by Ademe is ongoing.
+.Sh AUTHORS
+.Nm
+was written and is maintained by
+.An |M\['e]so|Star> Aq Mt contact@meso-star.com .
diff --git a/solmaps.c b/solmaps.c
@@ -1,75 +0,0 @@
-/* Copyright (C) 2017, 2018, 2025 |Méso|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/>. */
-
-#include "solpp.h"
-
-int
-main(int argc, char** argv)
-{
- char s[128];
- buf_char_T buf = BUF_NULL;
- FILE* input = stdin;
- char* line = NULL;
- double azim = -1;
- double elev = -1;
-
- if(argc > 1 && !(input = fopen(argv[1], "r"))) {
- fprintf(stderr, "Could not open the file `%s'.\n", argv[1]);
- return 1;
- }
-
- line = read_line(&buf, input);
- while(line) {
- if(!strncmp(line, "#--- Sun direction:", 19)) {
- /* Get the solar direction */
- CHK(sscanf(line+19, "%lf %lf (%*f %*f %*f)", &azim, &elev)==2);
- line = read_line(&buf, input);
- } else if(!strncmp(line, "# vtk", 5)) {
- char* header = NULL;
- char* rcv_name = NULL;
- FILE* output;
-
- CHK(azim >= 0 && elev >= 0);
-
- CHK(header = strdup(line)); /* Duplicate the current line */
- CHK(line = read_line(&buf, input));
- CHK(rcv_name = strdup(line)); /* Duplicate the line of the receiver name */
-
- /* Create the name of the destination file */
- CHK(snprintf(s, sizeof(s), "%g-%g-%s.vtk", azim, elev, rcv_name) < sizeof(s));
- printf("Writing `%s'\n", s);
- CHK(output = fopen(s, "w"));
-
- /* Write the map data into `output' */
- fprintf(output, "%s\n", header);
- fprintf(output, "%s\n", rcv_name);
- while((line = read_line(&buf, input)) && line[0] != '#') {
- fprintf(output, "%s\n", line);
- }
-
- /* Clean up temporary variable and close the destination file */
- fclose(output);
- free(header);
- free(rcv_name);
- } else {
- line = read_line(&buf, input);
- }
- }
-
- BUF_RELEASE(buf);
- if(input && input!=stdin) fclose(input);
- return 0;
-}
-
diff --git a/solpaths.c b/solpaths.c
@@ -1,49 +0,0 @@
-/* Copyright (C) 2017, 2018, 2025 |Méso|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/>. */
-
-#include "solpp.h"
-
-int
-main(int argc, char** argv)
-{
- char s[128];
- buf_char_T buf = BUF_NULL;
- FILE* input = stdin;
- FILE* output = NULL;
- char* line = NULL;
- double azim = 0;
- double elev = 0;
-
- if(argc > 1 && !(input = fopen(argv[1], "r"))) {
- fprintf(stderr, "Could not open the file `%s'.\n", argv[1]);
- return 1;
- }
- while((line = read_line(&buf, input))) {
- if(strncmp(line, "#--- Sun direction:", 19)) {
- CHK(output != NULL);
- fprintf(output, "%s\n", line);
- } else {
- CHK(sscanf(line+19, "%lf %lf (%*f %*f %*f)", &azim, &elev) == 2);
- CHK(snprintf(s, sizeof(s), "%g-%g-paths.vtk", azim, elev) < sizeof(s));
- if(output) fclose(output);
- printf("Writing `%s'\n", s);
- CHK(output = fopen(s, "w"));
- }
- }
- BUF_RELEASE(buf);
- if(output) fclose(output);
- if(input && input!=stdin) fclose(input);
- return 0;
-}
diff --git a/solpp.c b/solpp.c
@@ -1,338 +0,0 @@
-/* Copyright (C) 2017, 2018, 2025 |Méso|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/>. */
-
-#include "solpp.h"
-
-struct mesh {
- BUF(double) coords;
- BUF(size_t) ids;
- BUF(size_t) entities;
- BUF(size_t) ncells;
- size_t voffset;
-};
-static const struct mesh MESH_NULL = {BUF_NULL, BUF_NULL, BUF_NULL, BUF_NULL, 0};
-
-static inline void
-mesh_release(struct mesh* msh)
-{
- BUF_RELEASE(msh->coords);
- BUF_RELEASE(msh->ids);
- BUF_RELEASE(msh->entities);
- BUF_RELEASE(msh->ncells);
- memset(msh, 0, sizeof(struct mesh));
-}
-
-static inline void
-mesh_write_vtk(FILE* output, struct mesh* msh)
-{
- size_t i, n;
-
- fprintf(output, "# vtk DataFile Version 2.0\n");
- fprintf(output, "Test\n");
- fprintf(output, "ASCII\n");
- fprintf(output, "DATASET POLYDATA\n");
-
- n = BUF_SZ(msh->coords)/3;
- fprintf(output, "POINTS %zu double\n", n);
- FOR_EACH(i, 0, n) {
- fprintf(output, "%g %g %g\n",
- BUF_AT(msh->coords, i*3+0),
- BUF_AT(msh->coords, i*3+1),
- BUF_AT(msh->coords, i*3+2));
- }
- n = BUF_SZ(msh->ids)/3;
- fprintf(output, "POLYGONS %zu %zu\n", n, n*4);
- FOR_EACH(i, 0, n) {
- fprintf(output, "3 %zu %zu %zu\n",
- BUF_AT(msh->ids, i*3+0),
- BUF_AT(msh->ids, i*3+1),
- BUF_AT(msh->ids, i*3+2));
- }
-}
-
-static inline void
-mesh_write_obj(FILE* output, struct mesh* msh)
-{
- size_t i, n;
-
- n = BUF_SZ(msh->coords)/3;
- FOR_EACH(i, 0, n) {
- fprintf(output, "v %g %g %g\n",
- BUF_AT(msh->coords, i*3+0),
- BUF_AT(msh->coords, i*3+1),
- BUF_AT(msh->coords, i*3+2));
- }
-
- n = BUF_SZ(msh->ids)/3;
- FOR_EACH(i, 0, n) {
- fprintf(output, "f %zu %zu %zu\n",
- BUF_AT(msh->ids, i*3+0) + 1,
- BUF_AT(msh->ids, i*3+1) + 1,
- BUF_AT(msh->ids, i*3+2) + 1);
- }
-}
-
-static inline void
-mesh_write_prim_data_vtk
- (FILE* output, const struct mesh* msh, struct simul* simul)
-{
- struct prim* prim;
- struct rcvXprim* rcvXprim;
- size_t ircv;
- size_t iprim;
- size_t icell;
- size_t n;
-
- n = BUF_SZ(msh->ids) / 3;
- fprintf(output, "CELL_DATA %zu\n", n);
-
- fprintf(output, "FIELD PrimaryData %zu\n", 2 + BUF_SZ(simul->rcvs)*6);
- fprintf(output, "cos_factor 2 %zu double\n", n);
- FOR_EACH(iprim, 0, BUF_SZ(msh->entities)) {
- CHK(prim = find_primary_by_id(simul, BUF_AT(msh->entities, iprim)));
- FOR_EACH(icell, 0, BUF_AT(msh->ncells, iprim)) {
- fprintf(output, "%g %g\n", prim->cos_factor.E, prim->cos_factor.SE);
- }
- }
-
- fprintf(output, "shadow_loss 2 %zu double\n", n);
- FOR_EACH(iprim, 0, BUF_SZ(msh->entities)) {
- CHK(prim = find_primary_by_id(simul, BUF_AT(msh->entities, iprim)));
- FOR_EACH(icell, 0, BUF_AT(msh->ncells, iprim)) {
- fprintf(output, "%g %g\n", prim->shadow_loss.E, prim->shadow_loss.SE);
- }
- }
-
- #define WRITE(Side, Name) { \
- fprintf(output, "%s_"STR(Side)"_"STR(Name)" 2 %zu double\n", rcv->name, n);\
- FOR_EACH(iprim, 0, BUF_SZ(msh->entities)) { \
- CHK(rcvXprim = find_rcvXprim(simul,rcv->id,BUF_AT(msh->entities,iprim)));\
- FOR_EACH(icell, 0, BUF_AT(msh->ncells, iprim)) { \
- fprintf(output, "%g %g\n", \
- rcvXprim->Name[Side].E, \
- rcvXprim->Name[Side].SE); \
- } \
- } \
- } (void)0
- FOR_EACH(ircv, 0, BUF_SZ(simul->rcvs)) {
- const struct rcv* rcv = &BUF_AT(simul->rcvs, ircv);
- WRITE(FRONT, in.flux);
- WRITE(FRONT, in.flux_mat_loss);
- WRITE(FRONT, in.flux_atm_loss);
- WRITE(BACK, in.flux);
- WRITE(BACK, in.flux_mat_loss);
- WRITE(BACK, in.flux_atm_loss);
- }
- #undef WRITE
-}
-
-static inline void
-mesh_write_rcv_data_vtk
- (FILE* output, const struct mesh* msh, struct simul* simul)
-{
- struct rcv* rcv;
- size_t ircv;
- size_t icell;
- size_t n;
-
- n = BUF_SZ(msh->ids)/3;
- fprintf(output, "CELL_DATA %zu\n", n);
- fprintf(output, "FIELD PrimaryData 12\n");
-
- #define WRITE(Side, Name) { \
- fprintf(output, STR(Side)"_"STR(Name)" 2 %zu double\n", n); \
- FOR_EACH(ircv, 0, BUF_SZ(msh->entities)) { \
- CHK(rcv = find_receiver_by_id(simul, BUF_AT(msh->entities, ircv))); \
- FOR_EACH(icell, 0, BUF_AT(msh->ncells, ircv)) { \
- fprintf(output, "%g %g\n", rcv->Name[Side].E, rcv->Name[Side].SE); \
- } \
- } \
- } (void)0
- WRITE(FRONT, in.flux);
- WRITE(FRONT, in.flux_mat_loss);
- WRITE(FRONT, in.flux_atm_loss);
- WRITE(FRONT, efficiency);
- WRITE(BACK, in.flux);
- WRITE(BACK, in.flux_mat_loss);
- WRITE(BACK, in.flux_atm_loss);
- WRITE(BACK, efficiency);
- #undef WRITE
-
- #define WRITE_MAP(Flux, Side) { \
- fprintf(output, STR(Side)"_"STR(Flux)"_map 2 %zu double\n", n); \
- FOR_EACH(ircv, 0, BUF_SZ(msh->entities)) { \
- CHK(rcv = find_receiver_by_id(simul, BUF_AT(msh->entities, ircv))); \
- if(!BUF_SZ(rcv->map[Flux][Side])) { \
- FOR_EACH(icell, 0, BUF_AT(msh->ncells,ircv)) fprintf(output,"-1 -1\n");\
- } else { \
- FOR_EACH(icell, 0, BUF_AT(msh->ncells,ircv)) { \
- const struct mc* mc = &BUF_AT(rcv->map[Flux][Side], icell); \
- fprintf(output, "%g %g\n", mc->E, mc->SE); \
- } \
- } \
- } \
- } (void)0
- WRITE_MAP(INCOMING, FRONT);
- WRITE_MAP(INCOMING, BACK);
- WRITE_MAP(ABSORBED, FRONT);
- WRITE_MAP(ABSORBED, BACK);
- #undef WRITE_MAP
-}
-
-int
-main(int argc, char** argv)
-{
- buf_char_T buf = BUF_NULL;
- char* line = NULL;
-
- FILE* input;
- FILE* geom;
- FILE* output;
-
- if(argc < 3) {
- fprintf(stderr, "Usage: %s solstice-geometry solstice-simulation\n", argv[0]);
- return 1;
- }
-
- CHK(geom = fopen(argv[1], "r"));
- CHK(input = fopen(argv[2], "r"));
-
- CHK(read_line(&buf, geom)); /* Skip the sun direction of the geometry */
- while((line = read_line(&buf, input))) {
- struct simul simul;
-
- const struct rcv* rcv = NULL;
- const struct prim* prim = NULL;
-
- struct mesh msh_rcv = MESH_NULL;
- struct mesh msh_prim = MESH_NULL;
- struct mesh msh_misc = MESH_NULL;
-
- char filename[128];
- char prefix[64];
- size_t ntris_grp = 0, nverts_grp = 0;
- size_t nverts_obj = 0, off_obj = 0, off_grp = 0;
-
- simul_init(&simul);
-
- CHK(!strncmp(line, "#--- Sun direction:", 19));
- CHK(sscanf(line+19, "%lf %lf (%*f %*f %*f)", &simul.azimuth, &simul.elevation)==2);
- CHK(snprintf(prefix, sizeof(prefix), "%g-%g-",
- simul.azimuth, simul.elevation) < sizeof(prefix));
- read_simulation(&simul, input);
-
- while((line = read_line(&buf, geom)) && strncmp(line, "#--- Sun", 8)) {
- if(!strncmp(line, "g ", 2)) {
- if(prim) {
- BUF_PUSH(msh_prim.ncells, ntris_grp);
- msh_prim.voffset += nverts_grp;
- }
- if(rcv) {
- BUF_PUSH(msh_rcv.ncells, ntris_grp);
- msh_rcv.voffset += nverts_grp;
- }
- if(!prim && !rcv) msh_misc.voffset += nverts_grp;
- prim = find_primary(&simul, line+2);
- rcv = find_receiver(&simul, line+2);
- if(prim) BUF_PUSH(msh_prim.entities, prim->id);
- if(rcv) BUF_PUSH(msh_rcv.entities, rcv->id);
- ntris_grp = 0;
- nverts_grp = 0;
- off_grp = 0;
- off_obj = nverts_obj;
-
- } else if(!strncmp(line, "v ", 2)) {
- double pos[3];
- CHK(sscanf(line+2, "%lf %lf %lf", pos+0, pos+1, pos+2) == 3);
- if(prim) {
- BUF_PUSH(msh_prim.coords, pos[0]);
- BUF_PUSH(msh_prim.coords, pos[1]);
- BUF_PUSH(msh_prim.coords, pos[2]);
- }
- if(rcv) {
- BUF_PUSH(msh_rcv.coords, pos[0]);
- BUF_PUSH(msh_rcv.coords, pos[1]);
- BUF_PUSH(msh_rcv.coords, pos[2]);
- }
- if(!prim && !rcv) {
- BUF_PUSH(msh_misc.coords, pos[0]);
- BUF_PUSH(msh_misc.coords, pos[1]);
- BUF_PUSH(msh_misc.coords, pos[2]);
- }
- ++nverts_grp;
- ++nverts_obj;
- } else if(!strncmp(line, "f ", 2)) {
- size_t tri[3];
- CHK(sscanf(line+2, "%zu %zu %zu", tri+0, tri+1, tri+2) == 3);
- if(prim) {
- BUF_PUSH(msh_prim.ids, tri[0]-1 + msh_prim.voffset + off_grp - off_obj);
- BUF_PUSH(msh_prim.ids, tri[1]-1 + msh_prim.voffset + off_grp - off_obj);
- BUF_PUSH(msh_prim.ids, tri[2]-1 + msh_prim.voffset + off_grp - off_obj);
- }
- if(rcv) {
- BUF_PUSH(msh_rcv.ids, tri[0]-1 + msh_rcv.voffset + off_grp - off_obj);
- BUF_PUSH(msh_rcv.ids, tri[1]-1 + msh_rcv.voffset + off_grp - off_obj);
- BUF_PUSH(msh_rcv.ids, tri[2]-1 + msh_rcv.voffset + off_grp - off_obj);
- }
- if(!prim && !rcv) {
- BUF_PUSH(msh_misc.ids, tri[0]-1 + msh_misc.voffset + off_grp - off_obj);
- BUF_PUSH(msh_misc.ids, tri[1]-1 + msh_misc.voffset + off_grp - off_obj);
- BUF_PUSH(msh_misc.ids, tri[2]-1 + msh_misc.voffset + off_grp - off_obj);
- }
- ++ntris_grp;
- } else if(!strcmp(line, "---")) {
- nverts_obj = 0;
- off_obj = 0;
- off_grp = nverts_grp;
- }
- }
- if(prim) BUF_PUSH(msh_prim.ncells, ntris_grp);
- if(rcv) BUF_PUSH(msh_rcv.ncells, ntris_grp);
-
- CHK(snprintf(filename, sizeof(filename),
- "%sprimaries.vtk", prefix) < sizeof(prefix));
- printf("Writing `%s'\n", filename);
- CHK(output = fopen(filename, "w"));
- mesh_write_vtk(output, &msh_prim);
- mesh_write_prim_data_vtk(output, &msh_prim, &simul);
- fclose(output);
-
- CHK(snprintf(filename, sizeof(filename),
- "%sreceivers.vtk", prefix) < sizeof(prefix));
- printf("Writing `%s'\n", filename);
- CHK(output = fopen(filename, "w"));
- mesh_write_vtk(output, &msh_rcv);
- mesh_write_rcv_data_vtk(output, &msh_rcv, &simul);
- fclose(output);
-
- CHK(snprintf(filename, sizeof(filename),
- "%smiscellaneous.obj", prefix) < sizeof(prefix));
- printf("Writing `%s'\n", filename);
- CHK(output = fopen(filename, "w"));
- mesh_write_obj(output, &msh_misc);
- fclose(output);
-
- mesh_release(&msh_prim);
- mesh_release(&msh_rcv);
- mesh_release(&msh_misc);
- simul_release(&simul);
- }
-
- fclose(geom);
- fclose(input);
- BUF_RELEASE(buf);
- return 0;
-}
-
diff --git a/solpp.h b/solpp.h
@@ -1,450 +0,0 @@
-/* Copyright (C) 2017, 2018, 2025 |Méso|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/>. */
-
-#define _POSIX_C_SOURCE 200809L /* Support of strdup */
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#ifndef SOLPP_H
-#define SOLPP_H
-
-enum side { FRONT, BACK };
-enum flux_density { ABSORBED, INCOMING };
-struct mc { double E/* Expected value */, SE/* Standard Error */; };
-
-/*******************************************************************************
- * Helper macros
- ******************************************************************************/
-#define STR__(X) #X
-#define STR(X) STR__(X)
-
-#define FOR_EACH(Id, Start, End) for((Id)=(Start); (Id)<(End); ++(Id))
-
-#define CHK(Cond) { \
- if(!(Cond)) { \
- fprintf(stderr, "error:%s:%d\n", __FILE__, __LINE__); \
- abort(); \
- } \
- } (void)0
-
-/*******************************************************************************
- * Dynamic buffer
- ******************************************************************************/
-#define BUF(Type) struct { Type* mem; size_t ca; size_t sz; }
-#define BUF_NULL {NULL, 0, 0}
-#define BUF_RELEASE(B) free((B).mem)
-#define BUF_RESERVE(B, Sz) { \
- if((Sz)>(B).ca) { \
- CHK((B).mem = realloc((B).mem, sizeof(*(B).mem)*((B).ca=(Sz)))); \
- } \
- }(void)0
-#define BUF_RESIZE(B, Sz) {BUF_RESERVE((B), Sz); (B).sz = Sz;} (void)0
-#define BUF_PUSH(B, E) { \
- if((B).sz >= (B).ca) BUF_RESERVE((B), ((B).ca?(B).ca*2:32)); \
- (B).mem[(B).sz++]=(E); \
-} (void)0
-#define BUF_SZ(B) (B).sz
-#define BUF_MEM(B) (B).mem
-#define BUF_AT(B, I) (B).mem[I]
-
-/*******************************************************************************
- * Helper function
- ******************************************************************************/
-typedef BUF(char) buf_char_T;
-
-static inline char*
-read_line(buf_char_T* b, FILE* stream)
-{
- char* c;
-
- if(!BUF_SZ(*b)) BUF_RESIZE(*b, 32);
- if(!fgets(BUF_MEM(*b), (int)BUF_SZ(*b), stream)) return NULL;
-
- /* Ensure that the whole line is read */
- while(!strrchr(BUF_MEM(*b), '\n') && !feof(stream)) {
- BUF_RESIZE(*b, BUF_SZ(*b)+32);
- CHK(fgets
- (BUF_MEM(*b) + strlen(BUF_MEM(*b)),
- (int)(BUF_SZ(*b) - strlen(BUF_MEM(*b))), stream));
- }
-
- /* Remove the carriage return */
- if((c = strrchr(BUF_MEM(*b), '\n'))) *c = '\0';
-
- return BUF_MEM(*b);
-}
-
-/*******************************************************************************
- * Per receiver double sided estimations
- ******************************************************************************/
-struct flux {
- struct mc flux[2];
- struct mc flux_no_mat_loss[2];
- struct mc flux_no_atm_loss[2];
- struct mc flux_mat_loss[2];
- struct mc flux_atm_loss[2];
-};
-
-struct rcv {
- struct flux in;
- struct flux abs;
- struct mc efficiency[2];
- BUF(struct mc) map[2][2]; /* Absorbed/Incoming, Front/Back */
- char* name;
- size_t id;
- double area;
-};
-
-static inline void rcv_init(struct rcv* r) {memset(r, 0, sizeof(*r));}
-static inline void rcv_release(struct rcv* r)
-{
- free(r->name);
- BUF_RELEASE(r->map[ABSORBED][FRONT]);
- BUF_RELEASE(r->map[ABSORBED][BACK]);
- BUF_RELEASE(r->map[INCOMING][FRONT]);
- BUF_RELEASE(r->map[INCOMING][BACK]);
-}
-
-/*******************************************************************************
- * Per primary estimations
- ******************************************************************************/
-struct prim {
- struct mc cos_factor;
- struct mc shadow_loss;
- char* name;
- size_t id;
- size_t nsamps;
- double area;
-};
-static inline void prim_init(struct prim* p) {memset(p, 0, sizeof(*p));}
-static inline void prim_release(struct prim* p) {free(p->name);}
-
-/*******************************************************************************
- * Per receiver X primary estimations
- ******************************************************************************/
-struct rcvXprim {
- struct flux in;
- struct flux abs;
- size_t rcv_id;
- size_t prim_id;
-};
-static inline void rcvXprim_init(struct rcvXprim* r) {memset(r, 0, sizeof(*r));}
-static inline void rcvXprim_release(struct rcvXprim* r) { /*Do nothing*/ }
-
-/*******************************************************************************
- * Overall estimations of a simulation
- ******************************************************************************/
-struct simul {
- struct mc potential_flux;
- struct mc absorbed_flux;
- struct mc cos_factor;
- struct mc shadow_loss;
- struct mc missing_loss;
- struct mc materials_loss;
- struct mc atmospheric_loss;
-
- BUF(struct rcv) rcvs;
- BUF(struct prim) prims;
- BUF(struct rcvXprim) rcvXprims;
-
- double azimuth;
- double elevation;
- size_t nsamps;
-};
-
-static inline void simul_init(struct simul* s) {memset(s, 0, sizeof(*s));}
-
-static inline void
-simul_release(struct simul* s)
-{
- size_t i;
- FOR_EACH(i,0,BUF_SZ(s->rcvs)) rcv_release(&BUF_AT(s->rcvs, i));
- FOR_EACH(i,0,BUF_SZ(s->prims)) prim_release(&BUF_AT(s->prims, i));
- FOR_EACH(i,0,BUF_SZ(s->rcvXprims)) rcvXprim_release(&BUF_AT(s->rcvXprims, i));
- BUF_RELEASE(s->rcvs);
- BUF_RELEASE(s->prims);
- BUF_RELEASE(s->rcvXprims);
-}
-
-/*******************************************************************************
- * Look for entities
- ******************************************************************************/
-static inline struct prim*
-find_primary(struct simul* s, const char* name)
-{
- size_t i;
- FOR_EACH(i, 0, BUF_SZ(s->prims))
- if(!strcmp(BUF_AT(s->prims, i).name, name)) return &BUF_AT(s->prims, i);
- return NULL;
-}
-
-static inline struct prim*
-find_primary_by_id(struct simul* s, const size_t id)
-{
- size_t i;
- FOR_EACH(i, 0, BUF_SZ(s->prims))
- if(BUF_AT(s->prims, i).id == id) return &BUF_AT(s->prims, i);
- return NULL;
-}
-
-static inline struct rcv*
-find_receiver(struct simul* s, const char* name)
-{
- size_t i;
- FOR_EACH(i, 0, BUF_SZ(s->rcvs))
- if(!strcmp(BUF_AT(s->rcvs, i).name, name)) return &BUF_AT(s->rcvs, i);
- return NULL;
-}
-
-static inline struct rcv*
-find_receiver_by_id(struct simul* s, const size_t id)
-{
- size_t i;
- FOR_EACH(i, 0, BUF_SZ(s->rcvs))
- if(BUF_AT(s->rcvs, i).id == id) return &BUF_AT(s->rcvs, i);
- return NULL;
-}
-
-static inline struct rcvXprim*
-find_rcvXprim(struct simul* s, const size_t rcv_id, const size_t prim_id)
-{
- size_t i;
- FOR_EACH(i, 0, BUF_SZ(s->rcvXprims)) {
- struct rcvXprim* rXp = &BUF_AT(s->rcvXprims, i);
- if(rXp->rcv_id == rcv_id && rXp->prim_id == prim_id) return rXp;
- }
- return NULL;
-}
-
-/*******************************************************************************
- * Read simulation data from a solstice-output
- ******************************************************************************/
-static inline void
-read_receiver_map_side_data(struct rcv* rcv, const size_t n, FILE* input)
-{
- buf_char_T buf = BUF_NULL;
- char* line = NULL;
- size_t i;
- enum side side;
- enum flux_density flux;
- const char* str;
-
- CHK(line = read_line(&buf, input));
- str = line + 8;
- if(!strncmp(str, "Front_faces", 11)) { side = FRONT; str += 12; }
- else if(!strncmp(str, "Back_faces", 10)) { side = BACK; str += 11; }
- else { fprintf(stderr, "Unexpected side name\n"); abort(); }
-
- if(!strncmp(str, "Incoming_flux", 13)) { flux = INCOMING; }
- else if(!strncmp(str, "Absorbed_flux", 13)) { flux = ABSORBED; }
- else { fprintf(stderr, "Unexpected flux name\n"); abort(); }
-
- CHK(read_line(&buf, input)); /* Discard "LOOKUP_TABLE default" line */
-
- BUF_RESIZE(rcv->map[flux][side], n);
- FOR_EACH(i, 0, n) {
- struct mc* mc = &BUF_AT(rcv->map[flux][side], i);
- CHK(line = read_line(&buf, input));
- CHK(sscanf(line, "%lf %lf", &mc->E, &mc->SE) == 2);
- }
-
- BUF_RELEASE(buf);
-}
-
-static inline void
-read_receiver_map(struct simul* simul, FILE* input)
-{
- struct rcv* rcv;
- buf_char_T buf = BUF_NULL;
- char* line = NULL;
- size_t i, n;
- long fp;
-
- CHK(line = read_line(&buf, input));
- CHK(rcv = find_receiver(simul, line));
-
- /* Skip header */
- CHK(read_line(&buf, input));
- CHK(read_line(&buf, input));
- /* Skip vertices */
- CHK(line = read_line(&buf, input));
- CHK(sscanf(line, "POINTS %zu float", &n) == 1);
- FOR_EACH(i, 0, n) CHK(read_line(&buf, input));
- /* Skip polygons */
- CHK(line = read_line(&buf, input));
- CHK(sscanf(line, "POLYGONS %zu %*u", &n) == 1);
- FOR_EACH(i, 0, n) CHK(read_line(&buf, input));
- /* Read the map data of one side */
- CHK(line = read_line(&buf, input));
- CHK(sscanf(line, "CELL_DATA %zu", &n) == 1);
- /* Read map data */
- do {
- read_receiver_map_side_data(rcv, n, input);
- fp = ftell(input);
- line = read_line(&buf, input);
- fseek(input, fp, SEEK_SET);
- } while(line && !strncmp(line, "SCALARS", 7));
-
- BUF_RELEASE(buf);
-}
-
-static inline void
-read_simulation(struct simul* simul, FILE* input)
-{
- buf_char_T buf = BUF_NULL;
- char* line = NULL;
- char* tk = NULL;
- size_t nrcvs, nprims;
- size_t i;
-
- /* Counters */
- CHK(line = read_line(&buf, input));
- CHK(sscanf(line, "%*u %zu %zu %zu %*u", &nrcvs, &nprims, &simul->nsamps)==3);
-
- /* Global results */
- #define READ(Name) { \
- CHK(line = read_line(&buf, input)); \
- CHK(sscanf(line, "%lf %lf", &simul->Name.E, &simul->Name.SE) == 2); \
- } (void)0
- READ(potential_flux);
- READ(absorbed_flux);
- READ(cos_factor);
- READ(shadow_loss);
- READ(missing_loss);
- READ(materials_loss);
- READ(atmospheric_loss);
- #undef READ
-
- /* Read per receiver results */
- BUF_RESIZE(simul->rcvs, nrcvs);
- FOR_EACH(i, 0, nrcvs) {
- struct rcv* rcv = &BUF_AT(simul->rcvs, i);
- rcv_init(rcv);
-
- CHK(line = read_line(&buf, input));
- CHK(tk = strtok(line, " \t"));
- CHK(rcv->name = strdup(tk));
-
- CHK(tk = strtok(NULL, ""));
- #define GET(Side, Name) &rcv->Name[Side].E, &rcv->Name[Side].SE
- CHK(sscanf
- (tk,
- "%zu %lf "
- "%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf "
- "%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf "
- "%lf %lf "
- "%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf "
- "%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf "
- "%lf %lf",
- &rcv->id, &rcv->area,
- GET(FRONT, in.flux),
- GET(FRONT, in.flux_no_mat_loss),
- GET(FRONT, in.flux_no_atm_loss),
- GET(FRONT, in.flux_mat_loss),
- GET(FRONT, in.flux_atm_loss),
- GET(FRONT, abs.flux),
- GET(FRONT, abs.flux_no_mat_loss),
- GET(FRONT, abs.flux_no_atm_loss),
- GET(FRONT, abs.flux_mat_loss),
- GET(FRONT, abs.flux_atm_loss),
- GET(FRONT, efficiency),
- GET(BACK, in.flux),
- GET(BACK, in.flux_no_mat_loss),
- GET(BACK, in.flux_no_atm_loss),
- GET(BACK, in.flux_mat_loss),
- GET(BACK, in.flux_atm_loss),
- GET(BACK, abs.flux),
- GET(BACK, abs.flux_no_mat_loss),
- GET(BACK, abs.flux_no_atm_loss),
- GET(BACK, abs.flux_mat_loss),
- GET(BACK, abs.flux_atm_loss),
- GET(BACK, efficiency)) == 46);
- #undef GET
- }
-
- /* Read per primary results */
- BUF_RESIZE(simul->prims, nprims);
- FOR_EACH(i, 0, nprims) {
- struct prim* prim = &BUF_AT(simul->prims, i);
- prim_init(prim);
-
- CHK(line = read_line(&buf, input));
- CHK(tk = strtok(line, " \t"));
- CHK(prim->name = strdup(tk));
-
- CHK(tk = strtok(NULL, ""));
- CHK(sscanf(tk, "%zu %lf %zu %lf %lf %lf %lf",
- &prim->id, &prim->area, &prim->nsamps,
- &prim->cos_factor.E, &prim->cos_factor.SE,
- &prim->shadow_loss.E, &prim->shadow_loss.SE) == 7);
- }
-
- /* Per receiverXprimary results */
- BUF_RESIZE(simul->rcvXprims, nprims*nrcvs);
- FOR_EACH(i, 0, nprims*nrcvs) {
- struct rcvXprim* rcvXprim = &BUF_AT(simul->rcvXprims, i);
- rcvXprim_init(rcvXprim);
-
- CHK(line = read_line(&buf, input));
- #define GET(Side, Name) &rcvXprim->Name[Side].E, &rcvXprim->Name[Side].SE
- CHK(sscanf
- (line,
- "%zu %zu "
- "%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf "
- "%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf "
- "%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf "
- "%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf",
- &rcvXprim->rcv_id, &rcvXprim->prim_id,
- GET(FRONT, in.flux),
- GET(FRONT, in.flux_no_mat_loss),
- GET(FRONT, in.flux_no_atm_loss),
- GET(FRONT, in.flux_mat_loss),
- GET(FRONT, in.flux_atm_loss),
- GET(FRONT, abs.flux),
- GET(FRONT, abs.flux_no_mat_loss),
- GET(FRONT, abs.flux_no_atm_loss),
- GET(FRONT, abs.flux_mat_loss),
- GET(FRONT, abs.flux_atm_loss),
- GET(BACK, in.flux),
- GET(BACK, in.flux_no_mat_loss),
- GET(BACK, in.flux_no_atm_loss),
- GET(BACK, in.flux_mat_loss),
- GET(BACK, in.flux_atm_loss),
- GET(BACK, abs.flux),
- GET(BACK, abs.flux_no_mat_loss),
- GET(BACK, abs.flux_no_atm_loss),
- GET(BACK, abs.flux_mat_loss),
- GET(BACK, abs.flux_atm_loss)) == 42);
- #undef GET
- }
-
- /* Read receiver maps */
- for(;;) {
- const long fp = ftell(input);
- line = read_line(&buf, input);
-
- if(line && !strncmp(line, "# vtk", 5)) {
- read_receiver_map(simul, input);
- } else {
- fseek(input, fp, SEEK_SET);
- break;
- }
- }
- BUF_RELEASE(buf);
-}
-
-#endif /* SOLPP_H */
-
diff --git a/solppraw.c b/solppraw.c
@@ -1,165 +0,0 @@
-/* Copyright (C) 2017, 2018, 2025 |Méso|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/>. */
-
-#include "solpp.h"
-
-static inline void
-print_rcv_side(FILE* output, const enum side side, struct rcv* rcv)
-{
- #define W(Name) rcv->Name[side].E, rcv->Name[side].SE
- fprintf(output, " | [Incoming] ");
- fprintf(output, " [Absorbed] \n");
- fprintf(output, " Flux | %16g +/- %-16g | %16g +/- %-16g\n",
- W(in.flux), W(abs.flux));
- fprintf(output, " Material loss | %16g +/- %-16g | %16g +/- %-16g\n",
- W(in.flux_mat_loss), W(abs.flux_mat_loss));
- fprintf(output, "Atmospheric loss | %16g +/- %-16g | %16g +/- %-16g\n",
- W(in.flux_atm_loss), W(abs.flux_atm_loss));
- fprintf(output, "No Material loss | %16g +/- %-16g | %16g +/- %-16g\n",
- W(in.flux_no_mat_loss), W(abs.flux_no_mat_loss));
- fprintf(output, " No Atmos. loss | %16g +/- %-16g | %16g +/- %-16g\n",
- W(in.flux_no_atm_loss), W(abs.flux_no_atm_loss));
- fprintf(output, " |\n");
- fprintf(output, " Efficiency | %16g +/- %-16g\n", W(efficiency));
- #undef W
-}
-
-static inline void
-print_rcvXprim_side(FILE* output, const enum side side, struct rcvXprim* rXp)
-{
- #define W(Name) rXp->Name[side].E, rXp->Name[side].SE
- fprintf(output, " | [Incoming] ");
- fprintf(output, " [Absorbed] \n");
- fprintf(output, " Flux | %16g +/- %-16g | %16g +/- %-16g\n",
- W(in.flux), W(abs.flux));
- fprintf(output, " Material loss | %16g +/- %-16g | %16g +/- %-16g\n",
- W(in.flux_mat_loss), W(abs.flux_mat_loss));
- fprintf(output, "Atmospheric loss | %16g +/- %-16g | %16g +/- %-16g\n",
- W(in.flux_atm_loss), W(abs.flux_atm_loss));
- fprintf(output, "No Material loss | %16g +/- %-16g | %16g +/- %-16g\n",
- W(in.flux_no_mat_loss), W(abs.flux_no_mat_loss));
- fprintf(output, " No Atmos. loss | %16g +/- %-16g | %16g +/- %-16g\n",
- W(in.flux_no_atm_loss), W(abs.flux_no_atm_loss));
- #undef W
-}
-
-static inline void
-print_simulation(FILE* output, struct simul* simul)
-{
- size_t i;
-
- /* Global results */
- #define W(Name) simul->Name.E, simul->Name.SE
- fprintf(output, " Overall results (#Samples = %zu)\n", simul->nsamps);
- fprintf(output, "------------------------------------------------------------");
- fprintf(output, "------------------------------\n");
- fprintf(output, " Potential flux | %16g +/- %-16g\n", W(potential_flux));
- fprintf(output, " Absorbed flux | %16g +/- %-16g\n", W(absorbed_flux));
- fprintf(output, " Cosine factor | %16g +/- %-16g\n", W(cos_factor));
- fprintf(output, " Shadow loss | %16g +/- %-16g\n", W(shadow_loss));
- fprintf(output, " Missing loss | %16g +/- %-16g\n", W(missing_loss));
- fprintf(output, " Materials loss | %16g +/- %-16g\n", W(materials_loss));
- fprintf(output, "Atmospheric loss | %16g +/- %-16g\n", W(atmospheric_loss));
- fprintf(output, "\n");
- #undef W
- /* Per receivers results */
- FOR_EACH(i, 0, BUF_SZ(simul->rcvs)) {
- struct rcv* rcv = &BUF_AT(simul->rcvs, i);
- fprintf(output, " Receiver `%s' (Area = %g)\n", rcv->name, rcv->area);
- if(rcv->in.flux[FRONT].E >= 0) {
- fprintf(output, "-----------------------------------------------------[Front]");
- fprintf(output, "------------------------------\n");
- print_rcv_side(output, FRONT, rcv);
- }
- if(rcv->in.flux[BACK].E >= 0) {
- fprintf(output, "------------------------------------------------------[Back]");
- fprintf(output, "------------------------------\n");
- print_rcv_side(output, BACK, rcv);
- }
- fprintf(output, "\n");
- }
- /* Per primary results */
- FOR_EACH(i, 0, BUF_SZ(simul->prims)) {
- struct prim* prim = &BUF_AT(simul->prims, i);
- fprintf(output, " Primary `%s' (Area = %g; #Samples = %zu)\n",
- prim->name, prim->area, prim->nsamps);
- #define W(Name) prim->Name.E, prim->Name.SE
- fprintf(output, "------------------------------------------------------------");
- fprintf(output, "------------------------------\n");
- fprintf(output, " Cosine factor | %16g +/- %-16g\n", W(cos_factor));
- fprintf(output, " Shadow loss | %16g +/- %-16g\n", W(shadow_loss));
- fprintf(output, "\n");
- #undef W
- }
- /* Per receiverXprimary results */
- FOR_EACH(i, 0, BUF_SZ(simul->rcvXprims)) {
- struct rcvXprim* rXp = &BUF_AT(simul->rcvXprims, i);
- struct rcv* rcv = find_receiver_by_id(simul, rXp->rcv_id);
- struct prim* prim = find_primary_by_id(simul, rXp->prim_id);
- fprintf(output, " Receiver `%s' X Primary `%s'\n", rcv->name, prim->name);
- if(rXp->in.flux[FRONT].E >= 0) {
- fprintf(output, "-----------------------------------------------------[Front]");
- fprintf(output, "------------------------------\n");
- print_rcvXprim_side(output, FRONT, rXp);
- }
- if(rXp->in.flux[BACK].E >= 0) {
- fprintf(output, "------------------------------------------------------[Back]");
- fprintf(output, "------------------------------\n");
- print_rcvXprim_side(output, BACK, rXp);
- }
- fprintf(output, "\n");
- }
-}
-
-int
-main(int argc, char** argv)
-{
- char s[128];
- buf_char_T buf = BUF_NULL;
- FILE* input = stdin;
- char* line = NULL;
-
- if(argc > 1 && !(input = fopen(argv[1], "r"))) {
- fprintf(stderr, "Could not open the file `%s'.\n", argv[1]);
- return 1;
- }
- while((line = read_line(&buf, input))) {
- struct simul simul;
- FILE* output = NULL;
-
- if(strncmp(line, "#--- Sun direction:", 19)) continue;
-
- simul_init(&simul);
-
- CHK(!strncmp(line, "#--- Sun direction:", 19));
- CHK(sscanf(line+19, "%lf %lf (%*f %*f %*f)",
- &simul.azimuth, &simul.elevation)==2);
- CHK(snprintf(s, sizeof(s), "%g-%g-raw-results.txt",
- simul.azimuth, simul.elevation) < sizeof(s));
- read_simulation(&simul, input);
-
- printf("Writing `%s'\n", s);
- CHK(output = fopen(s, "w"));
- print_simulation(output, &simul);
- fclose(output);
-
- simul_release(&simul);
- }
-
- BUF_RELEASE(buf);
- if(input && input != stdin) fclose(input);
- return 0;
-}
-
diff --git a/src/solmaps.c b/src/solmaps.c
@@ -0,0 +1,92 @@
+/* Copyright (C) 2017, 2018, 2025 |Méso|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/>. */
+
+#include "solpp.h"
+
+#include <string.h>
+
+int
+main(int argc, char** argv)
+{
+ char s[128];
+ buf_char_T buf = BUF_NULL;
+ FILE* input = stdin;
+ char* line = NULL;
+ double azim, elev, lat, lon;
+ int case_dir = 0;
+ char time[64];
+ buf_char_T dont_read = BUF_NULL;
+
+ if(argc > 1 && !(input = fopen(argv[1], "r"))) {
+ fprintf(stderr, "Could not open the file `%s'.\n", argv[1]);
+ return 1;
+ }
+
+ line = read_line(&buf, input, &dont_read);
+
+ while(line) {
+ if(!strncmp(line, "#--- Sun direction:", 19)) {
+ case_dir = 1;
+ /* Get the solar direction */
+ CHK(sscanf(line+19, "%lf %lf (%*f %*f %*f)", &azim, &elev) == 2);
+ line = read_line(&buf, input, &dont_read);
+ } else if(!strncmp(line, "#--- Sun location and time:", 27)) {
+ /* Get the location and time */
+ case_dir = 0;
+ memset(time, 0, sizeof(time));
+ CHK(strlen(line+27) < sizeof(time));
+ CHK(sscanf(line+27, "%lf %lf %s (%*f %*f %*f)", &lat, &lon, time) == 3);
+ line = read_line(&buf, input, &dont_read);
+ } else if(!strncmp(line, "# vtk", 5)) {
+ char* header = NULL;
+ char* rcv_name = NULL;
+ FILE* output;
+
+ CHK(header = strdup(line)); /* Duplicate the current line */
+ CHK(line = read_line(&buf, input, &dont_read));
+ CHK(rcv_name = strdup(line)); /* Duplicate the line of the receiver name */
+
+ /* Create the name of the destination file */
+ if(case_dir) {
+ CHK(snprintf(s, sizeof(s), "%g-%g-%s.vtk", azim, elev, rcv_name)
+ < (long)sizeof(s));
+ } else {
+ CHK(snprintf(s, sizeof(s), "%g-%g-%s-%s.vtk", lat, lon, time, rcv_name)
+ < (long)sizeof(s));
+ }
+ printf("Writing `%s'\n", s);
+ CHK(output = fopen(s, "w"));
+
+ /* Write the map data into `output' */
+ fprintf(output, "%s\n", header);
+ fprintf(output, "%s\n", rcv_name);
+ while((line = read_line(&buf, input, &dont_read)) && line[0] != '#') {
+ fprintf(output, "%s\n", line);
+ }
+
+ /* Clean up temporary variable and close the destination file */
+ fclose(output);
+ free(header);
+ free(rcv_name);
+ } else {
+ line = read_line(&buf, input, &dont_read);
+ }
+ }
+
+ BUF_RELEASE(buf);
+ if(input && input!=stdin) fclose(input);
+ return 0;
+}
+
diff --git a/src/solpaths.c b/src/solpaths.c
@@ -0,0 +1,58 @@
+/* Copyright (C) 2017, 2018, 2025 |Méso|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/>. */
+
+#include "solpp.h"
+
+int
+main(int argc, char** argv)
+{
+ char s[128];
+ buf_char_T buf = BUF_NULL;
+ FILE* input = stdin;
+ FILE* output = NULL;
+ char* line = NULL;
+ double azim, elev, lat, lon;
+ char time[64];
+
+ if(argc > 1 && !(input = fopen(argv[1], "r"))) {
+ fprintf(stderr, "Could not open the file `%s'.\n", argv[1]);
+ return 1;
+ }
+ while((line = read_line(&buf, input, NULL))) {
+ if(!strncmp(line, "#--- Sun direction:", 19)) {
+ CHK(sscanf(line+19, "%lf %lf (%*f %*f %*f)", &azim, &elev) == 2);
+ CHK(snprintf(s, sizeof(s), "%g-%g-paths.vtk", azim, elev)
+ < (long)sizeof(s));
+ if(output) fclose(output);
+ printf("Writing `%s'\n", s);
+ CHK(output = fopen(s, "w"));
+ } else if(!strncmp(line, "#--- Sun location and time:", 27)) {
+ memset(time, 0, sizeof(time));
+ CHK(sscanf(line+27, "%lf %lf %s (%*f %*f %*f)", &lat, &lon, time) == 3);
+ CHK(snprintf(s, sizeof(s), "%g-%g-%s-paths.vtk", lat, lon, time)
+ < (long)sizeof(s));
+ if(output) fclose(output);
+ printf("Writing `%s'\n", s);
+ CHK(output = fopen(s, "w"));
+ } else {
+ CHK(output != NULL);
+ fprintf(output, "%s\n", line);
+ }
+ }
+ BUF_RELEASE(buf);
+ if(output) fclose(output);
+ if(input && input!=stdin) fclose(input);
+ return 0;
+}
diff --git a/src/solpp.c b/src/solpp.c
@@ -0,0 +1,352 @@
+/* Copyright (C) 2017, 2018, 2025 |Méso|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/>. */
+
+#include "solpp.h"
+
+struct mesh {
+ BUF(double) coords;
+ BUF(size_t) ids;
+ BUF(size_t) entities;
+ BUF(size_t) ncells;
+ size_t voffset;
+};
+static const struct mesh MESH_NULL = {BUF_NULL, BUF_NULL, BUF_NULL, BUF_NULL, 0};
+
+static inline void
+mesh_release(struct mesh* msh)
+{
+ BUF_RELEASE(msh->coords);
+ BUF_RELEASE(msh->ids);
+ BUF_RELEASE(msh->entities);
+ BUF_RELEASE(msh->ncells);
+ memset(msh, 0, sizeof(struct mesh));
+}
+
+static inline void
+mesh_write_vtk(FILE* output, struct mesh* msh)
+{
+ size_t i, n;
+
+ fprintf(output, "# vtk DataFile Version 2.0\n");
+ fprintf(output, "Test\n");
+ fprintf(output, "ASCII\n");
+ fprintf(output, "DATASET POLYDATA\n");
+
+ n = BUF_SZ(msh->coords)/3;
+ fprintf(output, "POINTS %zu double\n", n);
+ FOR_EACH(i, 0, n) {
+ fprintf(output, "%g %g %g\n",
+ BUF_AT(msh->coords, i*3+0),
+ BUF_AT(msh->coords, i*3+1),
+ BUF_AT(msh->coords, i*3+2));
+ }
+ n = BUF_SZ(msh->ids)/3;
+ fprintf(output, "POLYGONS %zu %zu\n", n, n*4);
+ FOR_EACH(i, 0, n) {
+ fprintf(output, "3 %zu %zu %zu\n",
+ BUF_AT(msh->ids, i*3+0),
+ BUF_AT(msh->ids, i*3+1),
+ BUF_AT(msh->ids, i*3+2));
+ }
+}
+
+static inline void
+mesh_write_obj(FILE* output, struct mesh* msh)
+{
+ size_t i, n;
+
+ n = BUF_SZ(msh->coords)/3;
+ FOR_EACH(i, 0, n) {
+ fprintf(output, "v %g %g %g\n",
+ BUF_AT(msh->coords, i*3+0),
+ BUF_AT(msh->coords, i*3+1),
+ BUF_AT(msh->coords, i*3+2));
+ }
+
+ n = BUF_SZ(msh->ids)/3;
+ FOR_EACH(i, 0, n) {
+ fprintf(output, "f %zu %zu %zu\n",
+ BUF_AT(msh->ids, i*3+0) + 1,
+ BUF_AT(msh->ids, i*3+1) + 1,
+ BUF_AT(msh->ids, i*3+2) + 1);
+ }
+}
+
+static inline void
+mesh_write_prim_data_vtk
+ (FILE* output, const struct mesh* msh, struct simul* simul)
+{
+ struct prim* prim;
+ struct rcvXprim* rcvXprim;
+ size_t ircv;
+ size_t iprim;
+ size_t icell;
+ size_t n;
+
+ n = BUF_SZ(msh->ids) / 3;
+ fprintf(output, "CELL_DATA %zu\n", n);
+
+ fprintf(output, "FIELD PrimaryData %zu\n", 2 + BUF_SZ(simul->rcvs)*6);
+ fprintf(output, "cos_factor 2 %zu double\n", n);
+ FOR_EACH(iprim, 0, BUF_SZ(msh->entities)) {
+ CHK(prim = find_primary_by_id(simul, BUF_AT(msh->entities, iprim)));
+ FOR_EACH(icell, 0, BUF_AT(msh->ncells, iprim)) {
+ fprintf(output, "%g %g\n", prim->cos_factor.E, prim->cos_factor.SE);
+ }
+ }
+
+ fprintf(output, "shadow_loss 2 %zu double\n", n);
+ FOR_EACH(iprim, 0, BUF_SZ(msh->entities)) {
+ CHK(prim = find_primary_by_id(simul, BUF_AT(msh->entities, iprim)));
+ FOR_EACH(icell, 0, BUF_AT(msh->ncells, iprim)) {
+ fprintf(output, "%g %g\n", prim->shadow_loss.E, prim->shadow_loss.SE);
+ }
+ }
+
+ #define WRITE(Side, Name) { \
+ fprintf(output, "%s_"STR(Side)"_"STR(Name)" 2 %zu double\n", rcv->name, n);\
+ FOR_EACH(iprim, 0, BUF_SZ(msh->entities)) { \
+ CHK(rcvXprim = find_rcvXprim(simul,rcv->id,BUF_AT(msh->entities,iprim)));\
+ FOR_EACH(icell, 0, BUF_AT(msh->ncells, iprim)) { \
+ fprintf(output, "%g %g\n", \
+ rcvXprim->Name[Side].E, \
+ rcvXprim->Name[Side].SE); \
+ } \
+ } \
+ } (void)0
+ FOR_EACH(ircv, 0, BUF_SZ(simul->rcvs)) {
+ const struct rcv* rcv = &BUF_AT(simul->rcvs, ircv);
+ WRITE(FRONT, in.flux);
+ WRITE(FRONT, in.flux_mat_loss);
+ WRITE(FRONT, in.flux_atm_loss);
+ WRITE(BACK, in.flux);
+ WRITE(BACK, in.flux_mat_loss);
+ WRITE(BACK, in.flux_atm_loss);
+ }
+ #undef WRITE
+}
+
+static inline void
+mesh_write_rcv_data_vtk
+ (FILE* output, const struct mesh* msh, struct simul* simul)
+{
+ struct rcv* rcv;
+ size_t ircv;
+ size_t icell;
+ size_t n;
+
+ n = BUF_SZ(msh->ids)/3;
+ fprintf(output, "CELL_DATA %zu\n", n);
+ fprintf(output, "FIELD PrimaryData 12\n");
+
+ #define WRITE(Side, Name) { \
+ fprintf(output, STR(Side)"_"STR(Name)" 2 %zu double\n", n); \
+ FOR_EACH(ircv, 0, BUF_SZ(msh->entities)) { \
+ CHK(rcv = find_receiver_by_id(simul, BUF_AT(msh->entities, ircv))); \
+ FOR_EACH(icell, 0, BUF_AT(msh->ncells, ircv)) { \
+ fprintf(output, "%g %g\n", rcv->Name[Side].E, rcv->Name[Side].SE); \
+ } \
+ } \
+ } (void)0
+ WRITE(FRONT, in.flux);
+ WRITE(FRONT, in.flux_mat_loss);
+ WRITE(FRONT, in.flux_atm_loss);
+ WRITE(FRONT, efficiency);
+ WRITE(BACK, in.flux);
+ WRITE(BACK, in.flux_mat_loss);
+ WRITE(BACK, in.flux_atm_loss);
+ WRITE(BACK, efficiency);
+ #undef WRITE
+
+ #define WRITE_MAP(Flux, Side) { \
+ fprintf(output, STR(Side)"_"STR(Flux)"_map 2 %zu double\n", n); \
+ FOR_EACH(ircv, 0, BUF_SZ(msh->entities)) { \
+ CHK(rcv = find_receiver_by_id(simul, BUF_AT(msh->entities, ircv))); \
+ if(!BUF_SZ(rcv->map[Flux][Side])) { \
+ FOR_EACH(icell, 0, BUF_AT(msh->ncells,ircv)) fprintf(output,"-1 -1\n");\
+ } else { \
+ FOR_EACH(icell, 0, BUF_AT(msh->ncells,ircv)) { \
+ const struct mc* mc = &BUF_AT(rcv->map[Flux][Side], icell); \
+ fprintf(output, "%g %g\n", mc->E, mc->SE); \
+ } \
+ } \
+ } \
+ } (void)0
+ WRITE_MAP(INCOMING, FRONT);
+ WRITE_MAP(INCOMING, BACK);
+ WRITE_MAP(ABSORBED, FRONT);
+ WRITE_MAP(ABSORBED, BACK);
+ #undef WRITE_MAP
+}
+
+int
+main(int argc, char** argv)
+{
+ buf_char_T buf = BUF_NULL;
+ char* line = NULL;
+ buf_char_T dont_read_i = BUF_NULL;
+ buf_char_T dont_read_g = BUF_NULL;
+ FILE* input;
+ FILE* geom;
+ FILE* output;
+
+ if(argc < 3) {
+ fprintf(stderr, "Usage: %s solstice-geometry solstice-simulation\n", argv[0]);
+ return 1;
+ }
+
+ CHK(geom = fopen(argv[1], "r"));
+ CHK(input = fopen(argv[2], "r"));
+
+ CHK(read_line(&buf, geom, &dont_read_g)); /* Skip the sun direction of the geometry */
+ while((line = read_line(&buf, input, &dont_read_i))) {
+ struct simul simul;
+
+ const struct rcv* rcv = NULL;
+ const struct prim* prim = NULL;
+
+ struct mesh msh_rcv = MESH_NULL;
+ struct mesh msh_prim = MESH_NULL;
+ struct mesh msh_misc = MESH_NULL;
+
+ char filename[128];
+ char prefix[64];
+ size_t ntris_grp = 0, nverts_grp = 0;
+ size_t nverts_obj = 0, off_obj = 0, off_grp = 0;
+
+ simul_init(&simul);
+
+ if(!strncmp(line, "#--- Sun direction:", 19)) {
+ CHK(sscanf(line+19, "%lf %lf (%*f %*f %*f)",
+ &simul.azimuth, &simul.elevation)==2);
+ CHK(snprintf(prefix, sizeof(prefix), "%g-%g-",
+ simul.azimuth, simul.elevation)
+ < (long)sizeof(prefix));
+ } else if(!strncmp(line, "#--- Sun location and time:", 27)) {
+ memset(simul.time, 0, sizeof(simul.time));
+ CHK(sscanf(line+27, "%lf %lf %s (%*f %*f %*f)",
+ &simul.latitude, &simul.longitude, simul.time) == 3);
+ CHK(snprintf(prefix, sizeof(prefix), "%g-%g-%s-",
+ simul.latitude, simul.longitude, simul.time)
+ < (long)sizeof(prefix));
+ }
+ read_simulation(&simul, input, &dont_read_i);
+
+ while((line = read_line(&buf, geom, &dont_read_g)) && strncmp(line, "#--- Sun", 8)) {
+ if(!strncmp(line, "g ", 2)) {
+ if(prim) {
+ BUF_PUSH(msh_prim.ncells, ntris_grp);
+ msh_prim.voffset += nverts_grp;
+ }
+ if(rcv) {
+ BUF_PUSH(msh_rcv.ncells, ntris_grp);
+ msh_rcv.voffset += nverts_grp;
+ }
+ if(!prim && !rcv) msh_misc.voffset += nverts_grp;
+ prim = find_primary(&simul, line+2);
+ rcv = find_receiver(&simul, line+2);
+ if(prim) { BUF_PUSH(msh_prim.entities, prim->id); }
+ if(rcv) { BUF_PUSH(msh_rcv.entities, rcv->id); }
+ ntris_grp = 0;
+ nverts_grp = 0;
+ off_grp = 0;
+ off_obj = nverts_obj;
+
+ } else if(!strncmp(line, "v ", 2)) {
+ double pos[3];
+ CHK(sscanf(line+2, "%lf %lf %lf", pos+0, pos+1, pos+2) == 3);
+ if(prim) {
+ BUF_PUSH(msh_prim.coords, pos[0]);
+ BUF_PUSH(msh_prim.coords, pos[1]);
+ BUF_PUSH(msh_prim.coords, pos[2]);
+ }
+ if(rcv) {
+ BUF_PUSH(msh_rcv.coords, pos[0]);
+ BUF_PUSH(msh_rcv.coords, pos[1]);
+ BUF_PUSH(msh_rcv.coords, pos[2]);
+ }
+ if(!prim && !rcv) {
+ BUF_PUSH(msh_misc.coords, pos[0]);
+ BUF_PUSH(msh_misc.coords, pos[1]);
+ BUF_PUSH(msh_misc.coords, pos[2]);
+ }
+ ++nverts_grp;
+ ++nverts_obj;
+ } else if(!strncmp(line, "f ", 2)) {
+ size_t tri[3];
+ CHK(sscanf(line+2, "%zu %zu %zu", tri+0, tri+1, tri+2) == 3);
+ if(prim) {
+ BUF_PUSH(msh_prim.ids, tri[0]-1 + msh_prim.voffset + off_grp - off_obj);
+ BUF_PUSH(msh_prim.ids, tri[1]-1 + msh_prim.voffset + off_grp - off_obj);
+ BUF_PUSH(msh_prim.ids, tri[2]-1 + msh_prim.voffset + off_grp - off_obj);
+ }
+ if(rcv) {
+ BUF_PUSH(msh_rcv.ids, tri[0]-1 + msh_rcv.voffset + off_grp - off_obj);
+ BUF_PUSH(msh_rcv.ids, tri[1]-1 + msh_rcv.voffset + off_grp - off_obj);
+ BUF_PUSH(msh_rcv.ids, tri[2]-1 + msh_rcv.voffset + off_grp - off_obj);
+ }
+ if(!prim && !rcv) {
+ BUF_PUSH(msh_misc.ids, tri[0]-1 + msh_misc.voffset + off_grp - off_obj);
+ BUF_PUSH(msh_misc.ids, tri[1]-1 + msh_misc.voffset + off_grp - off_obj);
+ BUF_PUSH(msh_misc.ids, tri[2]-1 + msh_misc.voffset + off_grp - off_obj);
+ }
+ ++ntris_grp;
+ } else if(!strcmp(line, "---")) {
+ nverts_obj = 0;
+ off_obj = 0;
+ off_grp = nverts_grp;
+ }
+ }
+ if(prim) { BUF_PUSH(msh_prim.ncells, ntris_grp); }
+ if(rcv) { BUF_PUSH(msh_rcv.ncells, ntris_grp); }
+
+ CHK(snprintf(filename, sizeof(filename), "%sprimaries.vtk", prefix)
+ < (long)sizeof(filename));
+ printf("Writing `%s'\n", filename);
+ CHK(output = fopen(filename, "w"));
+ mesh_write_vtk(output, &msh_prim);
+ mesh_write_prim_data_vtk(output, &msh_prim, &simul);
+ fclose(output);
+
+ CHK(snprintf(filename, sizeof(filename), "%sreceivers.vtk", prefix)
+ < (long)sizeof(filename));
+ printf("Writing `%s'\n", filename);
+ CHK(output = fopen(filename, "w"));
+ mesh_write_vtk(output, &msh_rcv);
+ mesh_write_rcv_data_vtk(output, &msh_rcv, &simul);
+ fclose(output);
+
+ CHK(snprintf(filename, sizeof(filename), "%smiscellaneous.obj", prefix)
+ < (long)sizeof(filename));
+ if(BUF_SZ(msh_misc.coords) == 0) {
+ printf("No miscellanous geometry to write `%s'\n", filename);
+ } else {
+ printf("Writing `%s'\n", filename);
+ CHK(output = fopen(filename, "w"));
+ mesh_write_obj(output, &msh_misc);
+ fclose(output);
+ }
+
+ mesh_release(&msh_prim);
+ mesh_release(&msh_rcv);
+ mesh_release(&msh_misc);
+ simul_release(&simul);
+ }
+
+ fclose(geom);
+ fclose(input);
+ BUF_RELEASE(buf);
+ return 0;
+}
diff --git a/src/solpp.h b/src/solpp.h
@@ -0,0 +1,470 @@
+/* Copyright (C) 2017, 2018, 2025 |Méso|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/>. */
+
+#define _POSIX_C_SOURCE 200809L /* Support of strdup */
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef SOLPP_H
+#define SOLPP_H
+
+enum side { FRONT, BACK };
+enum flux_density { ABSORBED, INCOMING };
+struct mc { double E/* Expected value */, SE/* Standard Error */; };
+
+/*******************************************************************************
+ * Helper macros
+ ******************************************************************************/
+#define STR__(X) #X
+#define STR(X) STR__(X)
+
+#define FOR_EACH(Id, Start, End) for((Id)=(Start); (Id)<(End); ++(Id))
+
+#define CHK(Cond) { \
+ if(!(Cond)) { \
+ fprintf(stderr, "error:%s:%d\n", __FILE__, __LINE__); \
+ abort(); \
+ } \
+ } (void)0
+
+/*******************************************************************************
+ * Dynamic buffer
+ ******************************************************************************/
+#define BUF(Type) struct { Type* mem; size_t ca; size_t sz; }
+#define BUF_NULL {NULL, 0, 0}
+#define BUF_RELEASE(B) free((B).mem)
+#define BUF_RESERVE(B, Sz) { \
+ if((Sz)>(B).ca) { \
+ CHK((B).mem = realloc((B).mem, sizeof(*(B).mem)*((B).ca=(Sz)))); \
+ } \
+ }(void)0
+#define BUF_RESIZE(B, Sz) {BUF_RESERVE((B), Sz); (B).sz = Sz;} (void)0
+#define BUF_PUSH(B, E) { \
+ if((B).sz >= (B).ca) {BUF_RESERVE((B), ((B).ca?(B).ca*2:32));} \
+ (B).mem[(B).sz++]=(E); \
+} (void)0
+#define BUF_SZ(B) (B).sz
+#define BUF_MEM(B) (B).mem
+#define BUF_AT(B, I) (B).mem[I]
+#define BUF_MOVE(D, S) (D).mem=(S).mem,(S).mem=NULL, \
+ (D).ca=(S).ca,(S).ca=0, \
+ (D).sz=(S).sz,(S).sz=0
+#define BUF_SAVE(B, Data, Sz) (B).mem=realloc((B).mem, (Sz)), \
+ memcpy((B).mem,(Data),sizeof(*(B).mem)*(Sz)), \
+ (B).sz=(B).ca=(Sz), \
+ (Data)=NULL
+
+/*******************************************************************************
+ * Helper function
+ ******************************************************************************/
+typedef BUF(char) buf_char_T;
+
+static inline char*
+read_line(buf_char_T* b, FILE* stream, buf_char_T* dont_read)
+{
+ char* c;
+
+ if(dont_read && BUF_MEM(*dont_read) != NULL) {
+ BUF_MOVE(*b, *dont_read);
+ return BUF_MEM(*b);
+ }
+
+ if(!BUF_SZ(*b)) { BUF_RESIZE(*b, 32); }
+ if(!fgets(BUF_MEM(*b), (int)BUF_SZ(*b), stream)) return NULL;
+
+ /* Ensure that the whole line is read */
+ while(!strrchr(BUF_MEM(*b), '\n') && !feof(stream)) {
+ BUF_RESIZE(*b, BUF_SZ(*b)+32);
+ CHK(fgets
+ (BUF_MEM(*b) + strlen(BUF_MEM(*b)),
+ (int)(BUF_SZ(*b) - strlen(BUF_MEM(*b))), stream));
+ }
+
+ /* Remove the carriage return */
+ if((c = strrchr(BUF_MEM(*b), '\n'))) *c = '\0';
+ return BUF_MEM(*b);
+}
+
+/*******************************************************************************
+ * Per receiver double sided estimations
+ ******************************************************************************/
+struct flux {
+ struct mc flux[2];
+ struct mc flux_no_mat_loss[2];
+ struct mc flux_no_atm_loss[2];
+ struct mc flux_mat_loss[2];
+ struct mc flux_atm_loss[2];
+};
+
+struct rcv {
+ struct flux in;
+ struct flux abs;
+ struct mc efficiency[2];
+ BUF(struct mc) map[2][2]; /* Absorbed/Incoming, Front/Back */
+ char* name;
+ size_t id;
+ double area;
+};
+
+static inline void rcv_init(struct rcv* r) {memset(r, 0, sizeof(*r));}
+static inline void rcv_release(struct rcv* r)
+{
+ free(r->name);
+ BUF_RELEASE(r->map[ABSORBED][FRONT]);
+ BUF_RELEASE(r->map[ABSORBED][BACK]);
+ BUF_RELEASE(r->map[INCOMING][FRONT]);
+ BUF_RELEASE(r->map[INCOMING][BACK]);
+}
+
+/*******************************************************************************
+ * Per primary estimations
+ ******************************************************************************/
+struct prim {
+ struct mc cos_factor;
+ struct mc shadow_loss;
+ char* name;
+ size_t id;
+ size_t nsamps;
+ double area;
+};
+static inline void prim_init(struct prim* p) {memset(p, 0, sizeof(*p));}
+static inline void prim_release(struct prim* p) {free(p->name);}
+
+/*******************************************************************************
+ * Per receiver X primary estimations
+ ******************************************************************************/
+struct rcvXprim {
+ struct flux in;
+ struct flux abs;
+ size_t rcv_id;
+ size_t prim_id;
+};
+static inline void rcvXprim_init(struct rcvXprim* r) {memset(r, 0, sizeof(*r));}
+static inline void rcvXprim_release(struct rcvXprim* r) { (void)r; /*Do nothing*/ }
+
+/*******************************************************************************
+ * Overall estimations of a simulation
+ ******************************************************************************/
+struct simul {
+ struct mc potential_flux;
+ struct mc absorbed_flux;
+ struct mc cos_factor;
+ struct mc shadow_loss;
+ struct mc missing_loss;
+ struct mc materials_loss;
+ struct mc atmospheric_loss;
+
+ BUF(struct rcv) rcvs;
+ BUF(struct prim) prims;
+ BUF(struct rcvXprim) rcvXprims;
+
+ double azimuth;
+ double elevation;
+ double latitude;
+ double longitude;
+ char time[64];
+ size_t nsamps;
+};
+
+static inline void simul_init(struct simul* s) {memset(s, 0, sizeof(*s));}
+
+static inline void
+simul_release(struct simul* s)
+{
+ size_t i;
+ FOR_EACH(i,0,BUF_SZ(s->rcvs)) rcv_release(&BUF_AT(s->rcvs, i));
+ FOR_EACH(i,0,BUF_SZ(s->prims)) prim_release(&BUF_AT(s->prims, i));
+ FOR_EACH(i,0,BUF_SZ(s->rcvXprims)) rcvXprim_release(&BUF_AT(s->rcvXprims, i));
+ BUF_RELEASE(s->rcvs);
+ BUF_RELEASE(s->prims);
+ BUF_RELEASE(s->rcvXprims);
+}
+
+/*******************************************************************************
+ * Look for entities
+ ******************************************************************************/
+static inline struct prim*
+find_primary(struct simul* s, const char* name)
+{
+ size_t i;
+ FOR_EACH(i, 0, BUF_SZ(s->prims))
+ if(!strcmp(BUF_AT(s->prims, i).name, name)) return &BUF_AT(s->prims, i);
+ return NULL;
+}
+
+static inline struct prim*
+find_primary_by_id(struct simul* s, const size_t id)
+{
+ size_t i;
+ FOR_EACH(i, 0, BUF_SZ(s->prims))
+ if(BUF_AT(s->prims, i).id == id) return &BUF_AT(s->prims, i);
+ return NULL;
+}
+
+static inline struct rcv*
+find_receiver(struct simul* s, const char* name)
+{
+ size_t i;
+ FOR_EACH(i, 0, BUF_SZ(s->rcvs))
+ if(!strcmp(BUF_AT(s->rcvs, i).name, name)) return &BUF_AT(s->rcvs, i);
+ return NULL;
+}
+
+static inline struct rcv*
+find_receiver_by_id(struct simul* s, const size_t id)
+{
+ size_t i;
+ FOR_EACH(i, 0, BUF_SZ(s->rcvs))
+ if(BUF_AT(s->rcvs, i).id == id) return &BUF_AT(s->rcvs, i);
+ return NULL;
+}
+
+static inline struct rcvXprim*
+find_rcvXprim(struct simul* s, const size_t rcv_id, const size_t prim_id)
+{
+ size_t i;
+ FOR_EACH(i, 0, BUF_SZ(s->rcvXprims)) {
+ struct rcvXprim* rXp = &BUF_AT(s->rcvXprims, i);
+ if(rXp->rcv_id == rcv_id && rXp->prim_id == prim_id) return rXp;
+ }
+ return NULL;
+}
+
+/*******************************************************************************
+ * Read simulation data from a solstice-output
+ ******************************************************************************/
+static inline void
+read_receiver_map_side_data
+ (struct rcv* rcv,
+ const size_t n,
+ FILE* input,
+ buf_char_T* dont_read)
+{
+ buf_char_T buf = BUF_NULL;
+ char* line = NULL;
+ size_t i;
+ enum side side;
+ enum flux_density flux;
+ const char* str;
+
+ CHK(line = read_line(&buf, input, dont_read));
+
+ str = line + 8;
+ if(!strncmp(str, "Front_faces", 11)) { side = FRONT; str += 12; }
+ else if(!strncmp(str, "Back_faces", 10)) { side = BACK; str += 11; }
+ else { fprintf(stderr, "Unexpected side name: '%s'\n", str); abort(); }
+
+ if(!strncmp(str, "Incoming_flux", 13)) { flux = INCOMING; }
+ else if(!strncmp(str, "Absorbed_flux", 13)) { flux = ABSORBED; }
+ else { fprintf(stderr, "Unexpected flux name: '%s'\n", str); abort(); }
+
+ CHK(read_line(&buf, input, dont_read)); /* Discard "LOOKUP_TABLE default" line */
+
+ BUF_RESIZE(rcv->map[flux][side], n);
+ FOR_EACH(i, 0, n) {
+ struct mc* mc = &BUF_AT(rcv->map[flux][side], i);
+ CHK(line = read_line(&buf, input, dont_read));
+ CHK(sscanf(line, "%lf %lf", &mc->E, &mc->SE) == 2);
+ }
+
+ BUF_RELEASE(buf);
+}
+
+static inline void
+read_receiver_map(struct simul* simul, FILE* input, buf_char_T* dont_read)
+{
+ struct rcv* rcv;
+ buf_char_T buf = BUF_NULL;
+ char* line = NULL;
+ size_t i, n;
+ int exit_loop;
+
+ CHK(line = read_line(&buf, input, dont_read));
+
+ CHK(rcv = find_receiver(simul, line));
+
+ /* Skip header */
+ CHK(read_line(&buf, input, dont_read));
+ CHK(read_line(&buf, input, dont_read));
+ /* Skip vertices */
+ CHK(line = read_line(&buf, input, dont_read));
+ CHK(sscanf(line, "POINTS %zu float", &n) == 1);
+ FOR_EACH(i, 0, n) { CHK(read_line(&buf, input, dont_read)); }
+ /* Skip polygons */
+ CHK(line = read_line(&buf, input, dont_read));
+ CHK(sscanf(line, "POLYGONS %zu %*u", &n) == 1);
+ FOR_EACH(i, 0, n) { CHK(read_line(&buf, input, dont_read)); }
+ /* Read the map data of one side */
+ CHK(line = read_line(&buf, input, dont_read));
+ CHK(sscanf(line, "CELL_DATA %zu", &n) == 1);
+ /* Read map data */
+ do {
+ read_receiver_map_side_data(rcv, n, input, dont_read);
+ line = read_line(&buf, input, dont_read);
+ exit_loop = (!line || strncmp(line, "SCALARS", 7));
+ if(line) { BUF_SAVE(*dont_read, line, 1+strlen(line)); }
+ } while(!exit_loop);
+
+ BUF_RELEASE(buf);
+}
+
+static inline void
+read_simulation(struct simul* simul, FILE* input, buf_char_T* dont_read)
+{
+ buf_char_T buf = BUF_NULL;
+ char* line = NULL;
+ char* tk = NULL;
+ size_t nrcvs, nprims;
+ size_t i;
+
+ /* Counters */
+ CHK(line = read_line(&buf, input, dont_read));
+ CHK(sscanf(line, "%*u %zu %zu %zu %*u", &nrcvs, &nprims, &simul->nsamps)==3);
+
+ /* Global results */
+ #define READ(Name) { \
+ CHK(line = read_line(&buf, input, dont_read)); \
+ CHK(sscanf(line, "%lf %lf", &simul->Name.E, &simul->Name.SE) == 2); \
+ } (void)0
+ READ(potential_flux);
+ READ(absorbed_flux);
+ READ(cos_factor);
+ READ(shadow_loss);
+ READ(missing_loss);
+ READ(materials_loss);
+ READ(atmospheric_loss);
+ #undef READ
+
+ /* Read per receiver results */
+ BUF_RESIZE(simul->rcvs, nrcvs);
+ FOR_EACH(i, 0, nrcvs) {
+ struct rcv* rcv = &BUF_AT(simul->rcvs, i);
+ rcv_init(rcv);
+
+ CHK(line = read_line(&buf, input, dont_read));
+ CHK(tk = strtok(line, " \t"));
+ CHK(rcv->name = strdup(tk));
+
+ CHK(tk = strtok(NULL, ""));
+ #define GET(Side, Name) &rcv->Name[Side].E, &rcv->Name[Side].SE
+ CHK(sscanf
+ (tk,
+ "%zu %lf "
+ "%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf "
+ "%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf "
+ "%lf %lf "
+ "%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf "
+ "%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf "
+ "%lf %lf",
+ &rcv->id, &rcv->area,
+ GET(FRONT, in.flux),
+ GET(FRONT, in.flux_no_mat_loss),
+ GET(FRONT, in.flux_no_atm_loss),
+ GET(FRONT, in.flux_mat_loss),
+ GET(FRONT, in.flux_atm_loss),
+ GET(FRONT, abs.flux),
+ GET(FRONT, abs.flux_no_mat_loss),
+ GET(FRONT, abs.flux_no_atm_loss),
+ GET(FRONT, abs.flux_mat_loss),
+ GET(FRONT, abs.flux_atm_loss),
+ GET(FRONT, efficiency),
+ GET(BACK, in.flux),
+ GET(BACK, in.flux_no_mat_loss),
+ GET(BACK, in.flux_no_atm_loss),
+ GET(BACK, in.flux_mat_loss),
+ GET(BACK, in.flux_atm_loss),
+ GET(BACK, abs.flux),
+ GET(BACK, abs.flux_no_mat_loss),
+ GET(BACK, abs.flux_no_atm_loss),
+ GET(BACK, abs.flux_mat_loss),
+ GET(BACK, abs.flux_atm_loss),
+ GET(BACK, efficiency)) == 46);
+ #undef GET
+ }
+
+ /* Read per primary results */
+ BUF_RESIZE(simul->prims, nprims);
+ FOR_EACH(i, 0, nprims) {
+ struct prim* prim = &BUF_AT(simul->prims, i);
+ prim_init(prim);
+
+ CHK(line = read_line(&buf, input, dont_read));
+ CHK(tk = strtok(line, " \t"));
+ CHK(prim->name = strdup(tk));
+
+ CHK(tk = strtok(NULL, ""));
+ CHK(sscanf(tk, "%zu %lf %zu %lf %lf %lf %lf",
+ &prim->id, &prim->area, &prim->nsamps,
+ &prim->cos_factor.E, &prim->cos_factor.SE,
+ &prim->shadow_loss.E, &prim->shadow_loss.SE) == 7);
+ }
+
+ /* Per receiverXprimary results */
+ BUF_RESIZE(simul->rcvXprims, nprims*nrcvs);
+ FOR_EACH(i, 0, nprims*nrcvs) {
+ struct rcvXprim* rcvXprim = &BUF_AT(simul->rcvXprims, i);
+ rcvXprim_init(rcvXprim);
+
+ CHK(line = read_line(&buf, input, dont_read));
+ #define GET(Side, Name) &rcvXprim->Name[Side].E, &rcvXprim->Name[Side].SE
+ CHK(sscanf
+ (line,
+ "%zu %zu "
+ "%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf "
+ "%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf "
+ "%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf "
+ "%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf",
+ &rcvXprim->rcv_id, &rcvXprim->prim_id,
+ GET(FRONT, in.flux),
+ GET(FRONT, in.flux_no_mat_loss),
+ GET(FRONT, in.flux_no_atm_loss),
+ GET(FRONT, in.flux_mat_loss),
+ GET(FRONT, in.flux_atm_loss),
+ GET(FRONT, abs.flux),
+ GET(FRONT, abs.flux_no_mat_loss),
+ GET(FRONT, abs.flux_no_atm_loss),
+ GET(FRONT, abs.flux_mat_loss),
+ GET(FRONT, abs.flux_atm_loss),
+ GET(BACK, in.flux),
+ GET(BACK, in.flux_no_mat_loss),
+ GET(BACK, in.flux_no_atm_loss),
+ GET(BACK, in.flux_mat_loss),
+ GET(BACK, in.flux_atm_loss),
+ GET(BACK, abs.flux),
+ GET(BACK, abs.flux_no_mat_loss),
+ GET(BACK, abs.flux_no_atm_loss),
+ GET(BACK, abs.flux_mat_loss),
+ GET(BACK, abs.flux_atm_loss)) == 42);
+ #undef GET
+ }
+
+ /* Read receiver maps */
+ for(;;) {
+ line = read_line(&buf, input, dont_read);
+
+ if(!line) break;
+ if(!strncmp(line, "# vtk", 5)) {
+ read_receiver_map(simul, input, dont_read);
+ } else {
+ BUF_SAVE(*dont_read, line, 1+strlen(line));
+ break;
+ }
+ }
+ BUF_RELEASE(buf);
+}
+
+#endif /* SOLPP_H */
+
diff --git a/src/solppraw.c b/src/solppraw.c
@@ -0,0 +1,182 @@
+/* Copyright (C) 2017, 2018, 2025 |Méso|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/>. */
+
+#include "solpp.h"
+
+static inline void
+print_rcv_side(FILE* output, const enum side side, struct rcv* rcv)
+{
+ #define W(Name) rcv->Name[side].E, rcv->Name[side].SE
+ fprintf(output, " | [Incoming] ");
+ fprintf(output, " [Absorbed] \n");
+ fprintf(output, " Flux | %16g +/- %-16g | %16g +/- %-16g\n",
+ W(in.flux), W(abs.flux));
+ fprintf(output, " Material loss | %16g +/- %-16g | %16g +/- %-16g\n",
+ W(in.flux_mat_loss), W(abs.flux_mat_loss));
+ fprintf(output, "Atmospheric loss | %16g +/- %-16g | %16g +/- %-16g\n",
+ W(in.flux_atm_loss), W(abs.flux_atm_loss));
+ fprintf(output, "No Material loss | %16g +/- %-16g | %16g +/- %-16g\n",
+ W(in.flux_no_mat_loss), W(abs.flux_no_mat_loss));
+ fprintf(output, " No Atmos. loss | %16g +/- %-16g | %16g +/- %-16g\n",
+ W(in.flux_no_atm_loss), W(abs.flux_no_atm_loss));
+ fprintf(output, " |\n");
+ fprintf(output, " Efficiency | %16g +/- %-16g\n", W(efficiency));
+ #undef W
+}
+
+static inline void
+print_rcvXprim_side(FILE* output, const enum side side, struct rcvXprim* rXp)
+{
+ #define W(Name) rXp->Name[side].E, rXp->Name[side].SE
+ fprintf(output, " | [Incoming] ");
+ fprintf(output, " [Absorbed] \n");
+ fprintf(output, " Flux | %16g +/- %-16g | %16g +/- %-16g\n",
+ W(in.flux), W(abs.flux));
+ fprintf(output, " Material loss | %16g +/- %-16g | %16g +/- %-16g\n",
+ W(in.flux_mat_loss), W(abs.flux_mat_loss));
+ fprintf(output, "Atmospheric loss | %16g +/- %-16g | %16g +/- %-16g\n",
+ W(in.flux_atm_loss), W(abs.flux_atm_loss));
+ fprintf(output, "No Material loss | %16g +/- %-16g | %16g +/- %-16g\n",
+ W(in.flux_no_mat_loss), W(abs.flux_no_mat_loss));
+ fprintf(output, " No Atmos. loss | %16g +/- %-16g | %16g +/- %-16g\n",
+ W(in.flux_no_atm_loss), W(abs.flux_no_atm_loss));
+ #undef W
+}
+
+static inline void
+print_simulation(FILE* output, struct simul* simul)
+{
+ size_t i;
+
+ /* Global results */
+ #define W(Name) simul->Name.E, simul->Name.SE
+ fprintf(output, " Overall results (#Samples = %zu)\n", simul->nsamps);
+ fprintf(output, "------------------------------------------------------------");
+ fprintf(output, "------------------------------\n");
+ fprintf(output, " Potential flux | %16g +/- %-16g\n", W(potential_flux));
+ fprintf(output, " Absorbed flux | %16g +/- %-16g\n", W(absorbed_flux));
+ fprintf(output, " Cosine factor | %16g +/- %-16g\n", W(cos_factor));
+ fprintf(output, " Shadow loss | %16g +/- %-16g\n", W(shadow_loss));
+ fprintf(output, " Missing loss | %16g +/- %-16g\n", W(missing_loss));
+ fprintf(output, " Materials loss | %16g +/- %-16g\n", W(materials_loss));
+ fprintf(output, "Atmospheric loss | %16g +/- %-16g\n", W(atmospheric_loss));
+ fprintf(output, "\n");
+ #undef W
+ /* Per receivers results */
+ FOR_EACH(i, 0, BUF_SZ(simul->rcvs)) {
+ struct rcv* rcv = &BUF_AT(simul->rcvs, i);
+ fprintf(output, " Receiver `%s' (Area = %g)\n", rcv->name, rcv->area);
+ if(rcv->in.flux[FRONT].E >= 0) {
+ fprintf(output, "-----------------------------------------------------[Front]");
+ fprintf(output, "------------------------------\n");
+ print_rcv_side(output, FRONT, rcv);
+ }
+ if(rcv->in.flux[BACK].E >= 0) {
+ fprintf(output, "------------------------------------------------------[Back]");
+ fprintf(output, "------------------------------\n");
+ print_rcv_side(output, BACK, rcv);
+ }
+ fprintf(output, "\n");
+ }
+ /* Per primary results */
+ FOR_EACH(i, 0, BUF_SZ(simul->prims)) {
+ struct prim* prim = &BUF_AT(simul->prims, i);
+ fprintf(output, " Primary `%s' (Area = %g; #Samples = %zu)\n",
+ prim->name, prim->area, prim->nsamps);
+ #define W(Name) prim->Name.E, prim->Name.SE
+ fprintf(output, "------------------------------------------------------------");
+ fprintf(output, "------------------------------\n");
+ fprintf(output, " Cosine factor | %16g +/- %-16g\n", W(cos_factor));
+ fprintf(output, " Shadow loss | %16g +/- %-16g\n", W(shadow_loss));
+ fprintf(output, "\n");
+ #undef W
+ }
+ /* Per receiverXprimary results */
+ FOR_EACH(i, 0, BUF_SZ(simul->rcvXprims)) {
+ struct rcvXprim* rXp = &BUF_AT(simul->rcvXprims, i);
+ struct rcv* rcv = find_receiver_by_id(simul, rXp->rcv_id);
+ struct prim* prim = find_primary_by_id(simul, rXp->prim_id);
+ fprintf(output, " Receiver `%s' X Primary `%s'\n", rcv->name, prim->name);
+ if(rXp->in.flux[FRONT].E >= 0) {
+ fprintf(output, "-----------------------------------------------------[Front]");
+ fprintf(output, "------------------------------\n");
+ print_rcvXprim_side(output, FRONT, rXp);
+ }
+ if(rXp->in.flux[BACK].E >= 0) {
+ fprintf(output, "------------------------------------------------------[Back]");
+ fprintf(output, "------------------------------\n");
+ print_rcvXprim_side(output, BACK, rXp);
+ }
+ fprintf(output, "\n");
+ }
+}
+
+int
+main(int argc, char** argv)
+{
+ char s[128];
+ buf_char_T buf = BUF_NULL;
+ FILE* input = stdin;
+ char* line = NULL;
+ buf_char_T dont_read = BUF_NULL;
+
+ if(argc > 1 && !(input = fopen(argv[1], "r"))) {
+ fprintf(stderr, "Could not open the file `%s'.\n", argv[1]);
+ return 1;
+ }
+ while((line = read_line(&buf, input, &dont_read))) {
+ struct simul simul;
+ FILE* output = NULL;
+
+ if(!strncmp(line, "#--- Sun direction:", 19)) {
+ simul_init(&simul);
+
+ CHK(sscanf(line+19, "%lf %lf (%*f %*f %*f)",
+ &simul.azimuth, &simul.elevation)==2);
+ CHK(snprintf(s, sizeof(s), "%g-%g-raw-results.txt",
+ simul.azimuth, simul.elevation) < (long)sizeof(s));
+ read_simulation(&simul, input, &dont_read);
+
+ printf("Writing `%s'\n", s);
+ CHK(output = fopen(s, "w"));
+ print_simulation(output, &simul);
+ fclose(output);
+
+ simul_release(&simul);
+ } else if(!strncmp(line, "#--- Sun location and time:", 27)) {
+
+ simul_init(&simul);
+
+ memset(simul.time, 0, sizeof(simul.time));
+ CHK(sscanf(line+27, "%lf %lf %s (%*f %*f %*f)",
+ &simul.latitude, &simul.longitude, simul.time) == 3);
+ CHK(snprintf(s, sizeof(s), "%g-%g-%s-",
+ simul.latitude, simul.longitude, simul.time)
+ < (long)sizeof(s));
+ read_simulation(&simul, input, &dont_read);
+
+ printf("Writing `%s'\n", s);
+ CHK(output = fopen(s, "w"));
+ print_simulation(output, &simul);
+ fclose(output);
+
+ simul_release(&simul);
+ }
+ }
+
+ BUF_RELEASE(buf);
+ if(input && input != stdin) fclose(input);
+ return 0;
+}
diff --git a/themis/Makefile b/themis/Makefile
@@ -19,7 +19,7 @@ include ../config.mk
NEXPERIMENTS = 1000000
NPATHS = 30
-SUN_DIRS = 270,30:160,40
+SUN_DIRS = 270,30 160,40
RCV = themis-rcv.yaml
INPUT = themis.yaml
@@ -29,13 +29,16 @@ SIMUL = simul
default: run
simul: $(INPUT) $(PROG)
- solstice -D$(SUN_DIRS) -n$(NEXPERIMENTS) -R$(RCV) -fo $(SIMUL) $(INPUT)
- solstice -D$(SUN_DIRS) -g format=obj:split=geometry -fo $(GEOM) $(INPUT)
+ solstice $$(for d in $(SUN_DIRS); do echo -f "-D$${d} "; done) \
+ -n$(NEXPERIMENTS) -R$(RCV) -fo $(SIMUL) $(INPUT)
+ solstice $$(for d in $(SUN_DIRS); do echo -f "-D$${d} "; done) \
+ -g format=obj:split=geometry -fo $(GEOM) $(INPUT)
../solppraw $(SIMUL)
../solpp $(GEOM) $(SIMUL)
paths: $(PROG) $(INPUT)
- solstice -D$(SUN_DIRS) -q -n$(NPATHS) -R$(RCV) -p default $(INPUT) | ../solpaths
+ solstice $$(for d in $(SUN_DIRS); do echo -f "-D$${d} "; done) \
+ -q -n$(NPATHS) -R$(RCV) -p default $(INPUT) | ../solpaths
run: paths simul
diff --git a/themis/themis.c b/themis/themis.c
@@ -1264,6 +1264,7 @@ main(int argc, char** argv)
const double themis_target[3] = { 0, 6.2, 87.15};/* to compute slant range */
const double corr = 4. ;/* correction length due to offset of the stl */
size_t i;
+ (void)argc; (void)argv;
/* Spectra */
print_section("Spectra");