solstice

Compute collected power and efficiencies of a solar plant
git clone git://git.meso-star.com/solstice.git
Log | Files | Refs | README | LICENSE

commit bad949dd877729bf006a90f38738d1b1a3bdfbfd
parent 1ab48c35659ad00482f91ccb2ce5798e96833260
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Mon,  7 Sep 2026 16:37:24 +0200

Merge branch 'release_0.11'

Diffstat:
MMakefile | 1+
MMakefile.core | 40+++++++++++++++++++++++++++++++---------
MREADME.md | 20++++++++++++++++----
Mconfig.mk | 3++-
Mdoc/solstice-input.5 | 53++++++++++++++++++++++++++++++++---------------------
Mdoc/solstice-output.5 | 30+++++++++++++++++-------------
Mdoc/solstice-receiver.5 | 4----
Mdoc/solstice.1.in | 134+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Msrc/main.c | 2+-
Msrc/parser/solparser.c | 2+-
Msrc/parser/solparser.h | 2+-
Msrc/parser/solparser_atmosphere.c | 2+-
Msrc/parser/solparser_atmosphere.h | 2+-
Msrc/parser/solparser_c.h | 2+-
Msrc/parser/solparser_entity.c | 2+-
Msrc/parser/solparser_entity.h | 2+-
Msrc/parser/solparser_geometry.c | 2+-
Msrc/parser/solparser_geometry.h | 2+-
Msrc/parser/solparser_image.c | 2+-
Msrc/parser/solparser_image.h | 2+-
Msrc/parser/solparser_material.c | 2+-
Msrc/parser/solparser_material.h | 2+-
Msrc/parser/solparser_medium.c | 2+-
Msrc/parser/solparser_medium.h | 2+-
Msrc/parser/solparser_mtl_data.c | 2+-
Msrc/parser/solparser_mtl_data.h | 2+-
Msrc/parser/solparser_pivot.c | 2+-
Msrc/parser/solparser_pivot.h | 2+-
Msrc/parser/solparser_shape.h | 2+-
Msrc/parser/solparser_spectrum.c | 2+-
Msrc/parser/solparser_spectrum.h | 2+-
Msrc/parser/solparser_sun.c | 2+-
Msrc/parser/solparser_sun.h | 2+-
Msrc/parser/test_solparser.c | 2+-
Msrc/parser/test_solparser2.c | 2+-
Msrc/parser/test_solparser3.c | 2+-
Msrc/parser/test_solparser4.c | 2+-
Msrc/parser/test_solparser5.c | 2+-
Msrc/parser/test_solparser6.c | 2+-
Msrc/parser/test_solparser7.c | 2+-
Msrc/parser/test_solparser8.c | 2+-
Msrc/parser/test_solparser_mirror.c | 2+-
Msrc/parser/test_solparser_normal_map.c | 2+-
Msrc/parser/test_solparser_spectrum.c | 2+-
Msrc/receivers/srcvl.c | 2+-
Msrc/receivers/srcvl.h | 2+-
Msrc/receivers/test_srcvl.c | 2+-
Msrc/receivers/test_srcvl2.c | 2+-
Msrc/score.h | 2+-
Msrc/solstice.c | 151+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/solstice.h | 40++++++++++++++++++++++++++++++++++++----
Msrc/solstice_args.c | 249+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/solstice_args.h.in | 39++++++++++++++++++++++++++++++++++++---
Msrc/solstice_atmosphere.c | 2+-
Msrc/solstice_c.h | 2+-
Msrc/solstice_draw.c | 2+-
Msrc/solstice_dump.c | 2+-
Msrc/solstice_entity.c | 2+-
Msrc/solstice_material.c | 2+-
Msrc/solstice_node.c | 2+-
Msrc/solstice_object.c | 2+-
Msrc/solstice_solve.c | 2+-
Msrc/solstice_spectrum.c | 2+-
Msrc/solstice_sun.c | 10++++++++--
Msrc/solstice_sun_spectrum.c | 2+-
Msrc/solstice_sun_spectrum.h | 2+-
Msrc/test_solstice_args.c | 237++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Msrc/test_solstice_simulation.c | 114+------------------------------------------------------------------------------
Asrc/test_solstice_simulation_time.c | 501+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_solstice_utils.h | 2+-
Myaml/test08.ref | 12++++++------
71 files changed, 1311 insertions(+), 435 deletions(-)

diff --git a/Makefile b/Makefile @@ -97,6 +97,7 @@ doc/solstice.1: doc/solstice.1.in -e 's/@SOLSTICE_ARGS_DEFAULT_IMG_WIDTH@/$(SOLSTICE_ARGS_DEFAULT_IMG_WIDTH)/' \ -e 's/@SOLSTICE_ARGS_DEFAULT_IMG_HEIGHT@/$(SOLSTICE_ARGS_DEFAULT_IMG_HEIGHT)/' \ -e 's/@SOLSTICE_ARGS_DEFAULT_IMG_SPP@/$(SOLSTICE_ARGS_DEFAULT_IMG_SPP)/' \ + -e 's/@SOLSTICE_ARGS_DEFAULT_SUN_DIRECTION_ALGORITHM@/$(SOLSTICE_ARGS_DEFAULT_SUN_DIRECTION_ALGORITHM)/' \ $@.in > $@ ################################################################################ diff --git a/Makefile.core b/Makefile.core @@ -81,6 +81,7 @@ src/solstice_args.h: src/.config_core src/solstice_args.h.in -e 's/@SOLSTICE_ARGS_DEFAULT_IMG_WIDTH@/$(SOLSTICE_ARGS_DEFAULT_IMG_WIDTH)/' \ -e 's/@SOLSTICE_ARGS_DEFAULT_IMG_HEIGHT@/$(SOLSTICE_ARGS_DEFAULT_IMG_HEIGHT)/' \ -e 's/@SOLSTICE_ARGS_DEFAULT_IMG_SPP@/$(SOLSTICE_ARGS_DEFAULT_IMG_SPP)/' \ + -e 's/@SOLSTICE_ARGS_DEFAULT_SUN_DIRECTION_ALGORITHM@/SOLSTICE_ARGS_SUN_DIRECTION_ALGORITHM_$(SOLSTICE_ARGS_DEFAULT_SUN_DIRECTION_ALGORITHM)/' \ $@.in > $@ src/solstice_version.h: src/.config_core src/solstice_version.h.in @@ -117,7 +118,8 @@ lint: ################################################################################ TEST_SRC =\ src/test_solstice_args.c\ - src/test_solstice_simulation.c + src/test_solstice_simulation.c\ + src/test_solstice_simulation_time.c TEST_OBJ =\ $(TEST_SRC:.c=.o) TEST_DEP =\ @@ -141,7 +143,7 @@ src/score-local.pc: score.pc.in src/.config_score_test # Regular cflags PKG_CONFIG_LOCAL = PKG_CONFIG_PATH="./src:$${PKG_CONFIG_PATH}" $(PKG_CONFIG) INCS_TEST = $$($(PKG_CONFIG_LOCAL) $(PCFLAGS) --cflags rsys score-local sprs-local srcv-local) -LIBS_TEST = $$($(PKG_CONFIG_LOCAL) $(PCFLAGS) --libs rsys score-local sprs-local srcv-local)\ +LIBS_TEST = $$($(PKG_CONFIG_LOCAL) $(PCFLAGS) --libs rsys score-local sprs-local srcv-local scem)\ -lm tests: library src/score-local.pc $(TEST_DEP) $(TEST_TGT) @@ -186,11 +188,11 @@ test: tests \ for i in $(TEST_SRC); do \ test="$$(basename "$${i}" ".c")"; \ - if [ "$${test}" != "test_solstice_simulation" ]; then \ - check "$${test}" "$${test}"; \ - else \ + if [ "$${test}" = "test_solstice_simulation" ]; then \ check test_solstice_simulation_beam_down \ test_solstice_simulation ./solstice yaml/ beam_down; \ + check test_solstice_simulation_test01 \ + test_solstice_simulation ./solstice yaml/ test01; \ check test_solstice_simulation_test02 \ test_solstice_simulation ./solstice yaml/ test02; \ check test_solstice_simulation_test03 \ @@ -203,10 +205,29 @@ test: tests test_solstice_simulation ./solstice yaml/ test06; \ check test_solstice_simulation_test07 \ test_solstice_simulation ./solstice yaml/ test07; \ - #check test_solstice_simulation_test08 \ - # test_solstice_simulation ./solstice yaml/ test08; \ - #check test_solstice_simulation_test09 \ - # test_solstice_simulation ./solstice yaml/ test09; \ + check test_solstice_simulation_test08 \ + test_solstice_simulation ./solstice yaml/ test08; \ + elif [ "$${test}" = "test_solstice_simulation_time" ]; then \ + check test_solstice_simulation_time_beam_down \ + test_solstice_simulation_time ./solstice yaml/ beam_down; \ + check test_solstice_simulation_time_test01 \ + test_solstice_simulation_time ./solstice yaml/ test01; \ + check test_solstice_simulation_time_test02 \ + test_solstice_simulation_time ./solstice yaml/ test02; \ + check test_solstice_simulation_time_test03 \ + test_solstice_simulation_time ./solstice yaml/ test03; \ + check test_solstice_simulation_time_test04 \ + test_solstice_simulation_time ./solstice yaml/ test04; \ + check test_solstice_simulation_time_test05 \ + test_solstice_simulation_time ./solstice yaml/ test05; \ + check test_solstice_simulation_time_test06 \ + test_solstice_simulation_time ./solstice yaml/ test06; \ + check test_solstice_simulation_time_test07 \ + test_solstice_simulation_time ./solstice yaml/ test07; \ + check test_solstice_simulation_time_test08 \ + test_solstice_simulation_time ./solstice yaml/ test08; \ + else \ + check "$${test}" "$${test}"; \ fi \ done; \ \ @@ -226,5 +247,6 @@ $(TEST_OBJ) : src/.config_score_test src/score-local.pc test_solstice_args\ test_solstice_simulation\ +test_solstice_simulation_time\ : src/.config_score_test src/score-local.pc $(LIBNAME) $(CC) $(CFLAGS_TEST) -o $@ src/$@.o $(LDFLAGS_TEST) diff --git a/README.md b/README.md @@ -36,11 +36,12 @@ features. ## How to build -This program, as part of the Solstice app, can be built on any POSIX system. +This program, as part of the Solstice app, can be built on any x86-64 POSIX +system. Note that you will most likely want to build the entire Solstice app rather than -this program alone. If so, a good starting point is the -[start-build](https://gitlab.com/meso-star/star-build) build system. +this library alone. If so, a good starting point is the dedicated +[Solstice web page](https://www.meso-star.com/solstice/install.html). This program depends on the [LibYAML](http://pyyaml.org/wiki/LibYAML), @@ -60,6 +61,17 @@ the project by running: ## Release notes +### Version 0.11 + +- Allow to define sun direction through location + time. + +- Add a command line option to set the sun direction algorithm. + Either meeus or psa (default) at this stage. + +- Add a command line option to set the DNI. + The sun's description in the input data already includes a mandatory dni + value. The new option allow to supersede this dni value from the command line. + ### Version 0.10 #### Raise the minimum required versions of companion libs @@ -252,7 +264,7 @@ radiative random walks bounce on many surfaces. ## License -Copyright (C) 2018-2026 |Meso|Star> (<contact@meso-star.com>). +Copyright (C) 2018-2026 |Méso|Star> (<contact@meso-star.com>). Copyright (C) 2016-2018 CNRS. Solstice is free software released under the GPL v3+ license: GNU GPL version 3 diff --git a/config.mk b/config.mk @@ -1,5 +1,5 @@ VERSION_MAJOR = 0 -VERSION_MINOR = 10 +VERSION_MINOR = 11 VERSION_PATCH = 0 VERSION = $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH) @@ -24,6 +24,7 @@ SOLSTICE_ARGS_DEFAULT_CAMERA_FOV = 70 SOLSTICE_ARGS_DEFAULT_IMG_WIDTH = 800 SOLSTICE_ARGS_DEFAULT_IMG_HEIGHT = 600 SOLSTICE_ARGS_DEFAULT_IMG_SPP = 1 +SOLSTICE_ARGS_DEFAULT_SUN_DIRECTION_ALGORITHM = psa ################################################################################ # Tools diff --git a/doc/solstice-input.5 b/doc/solstice-input.5 @@ -27,9 +27,9 @@ format is used by the program to represent a solar plant. It relies on the YAML 1.1 data serialization standard .Po -see -.Sx NOTES , -reference 1 +see the +.Sx NOTES +section, reference 1 .Pc ; assuming that the file is compatible with the .Nm @@ -318,9 +318,13 @@ The main .Em sun property is its direct normal irradiance, or .Em dni -in W\(md m\u\s-2\-2\s0\d. +in W/m². Its value is a scalar defining the direct irradiance received on a plane perpendicular to the main sun direction. +This value, even though mandatory, can be superseded by another value provided +by the +.Xr solstice 1 +command. The optional .Em spectrum parameter describes the per-wavelength distribution of the sun @@ -333,14 +337,14 @@ attribute is not defined, .Xr solstice 1 uses a default spectrum computed with the SMARTS software .Po -see -.Sx NOTES , -reference 2 +see the +.Sx NOTES +section, reference 2 .Pc between 0.28 and 4 micrometres. The total .Em dni -(integrated over the spectral range) was set to 1000 W\(md m\u\s-2\-2\s0\d. +(integrated over the spectral range) was set to 1000 W/m². The standard Mid-Latitude-Summer atmosphere was used with most gas concentrations set as default (CO2 concentration assumed 400 ppmv). .Pp @@ -388,8 +392,12 @@ value. .It Em buie The .Em buie -distribution, as first described in reference 3 -.Pq see Sx NOTES . +distribution +.Po +see the +.Sx NOTES +section, reference 3 +.Pc . Its single .Em csr parameter is the ratio between the circumsolar irradiance and the sum @@ -397,20 +405,23 @@ of the circumsolar and sun's disk irradiance. An analysis of typical .Em csr values can be found in reference 4 -.Pq see Sx NOTES . +.Po see the +.Sx NOTES +section +.Pc . .El .Sh ATMOSPHERE The .Em atmosphere , when provided, describes the medium surrounding the solar plant. -Its only parameter is its extinction coefficient in m\u\s-2\-1\s0\d, +Its only parameter is its extinction coefficient in m⁻¹, which can either be a scalar if the extinction is constant over the spectrum, or can be spectrally described. The extinction along light paths is only computed after the first reflector, as the sun description must include all atmospheric effects before the first reflector (see .Sx SUN -for more details). +section for more details). .Pp If no atmosphere is provided, atmospheric extinction after the first reflector is not taken into account. @@ -548,9 +559,9 @@ provide an optional .Em normal-map attribute that defines a path to a Portable PixMap image .Po -see +see the .Sx NOTES , -reference 5 +section, reference 5 .Pc whose pixels store a normal expressed in the tangent space of the interface. @@ -855,8 +866,12 @@ Four types of target are available: .It Em position The target is an absolute point in world coordinates. .It Em anchor -The target is a position relative to an entity (see -.Sx Anchor ) . +The target is a position relative to an entity +.Po +see the +.Sx ANCHOR +section +.Pc . .It Em sun The target is the center of the sun. .It Em direction @@ -1322,7 +1337,3 @@ 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/solstice-output.5 b/doc/solstice-output.5 @@ -69,7 +69,7 @@ are on a single line until a closing quote mark. | <rendering-output> # -r option <simulation-output> - ::= <sun-direction> + ::= <sun-specification> <counts> <global> [ <receivers-list> ] @@ -79,22 +79,27 @@ are on a single line until a closing quote mark. [ <simulation-output> ... ] <dump-geometry-output> - ::= <sun-direction> + ::= <sun-specification> <geometry-data> [ <dump-geometry-output> ... ] <dump-radiative-paths-output> - ::= <sun-direction> + ::= <sun-specification> VTK-RADIATIVE-PATHS [ <dump-radiative-paths-output> ... ] <rendering-output> - ::= <sun-direction> + ::= <sun-specification> PPM-FILE # ASCII PPM with 8-bits per component [1] [ <rendering-output> ... ] -.Ed -.Bd -literal -<sun-direction> ::= "#--- Sun direction: <alpha> <beta> (<sun-vector>)" + +<sun-specification> + ::= <sun-direction> + | <sun-time> + +<sun-direction> ::= "#--- Sun direction: <azimuth> <elevation> (<sun-vector>)" + +<sun-time> ::= "#--- Sun location and time: <lat> <long> <time> (<sun-vector>)" <counts> ::= "<#globals> <#receivers> <#primaries> <#samples> <#failed>" @@ -169,9 +174,12 @@ are on a single line until a closing quote mark. <area> ::= REAL # in ]0, INF) <real3> ::= REAL REAL REAL -<alpha> ::= REAL # Degrees in [0, 360[ -<beta> ::= REAL # Degrees in [0, 90] +<azimuth> ::= REAL # Degrees in [0, 360[ +<elevation> ::= REAL # Degrees in [0, 90] <sun-vector> ::= <real3> +<lat> ::= REAL # Degrees in [-90, +90] +<long> ::= REAL # Degrees in [-180, +180] +<time> ::= STRING # Same YYYY-MM-DDThh:mm:ss format as on the CLI <incoming-flux> ::= <estimate> <in-if-no-mat-loss> ::= <estimate> @@ -660,7 +668,3 @@ 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/solstice-receiver.5 b/doc/solstice-receiver.5 @@ -159,7 +159,3 @@ was initially developed with funding from the in collaboration with the PROMES Laboratory of the French National Centre for Scientific Research .Pq CNRS . -.Sh AUTHORS -.Nm -was written and is maintained by -.An |M\['e]so|Star> Aq Mt contact@meso-star.com . diff --git a/doc/solstice.1.in b/doc/solstice.1.in @@ -21,7 +21,6 @@ .Nd compute the power collected by a concentrated solar plant .Sh SYNOPSIS .Nm -.Nm .Op Ar option ... .Op Ar file .Nm @@ -90,11 +89,11 @@ The .Fl g option can be used to convert the .Xr solstice-input 5 -geometries into CAO files. +geometries into CAD files. The .Fl p option saves the sampled radiative paths used by the estimates, allowing -to visualise them externally, which may be a great help to identify a +the user to visualise them externally, which may be a great help to identify a design issue. Finally, the .Fl r @@ -104,26 +103,38 @@ they replace the default .Nm behaviour. .Pp -Any coordinate-related question in +All coordinates in .Nm -must be considered with the right-handed convention in mind. +follow the right-handed convention. .Sh OPTIONS .Bl -tag -width Ds -.It Fl D Ar alpha,beta Ns Op : Ns Ar ... -List of sun directions. -A direction is defined by two angles in degrees. -The first one, -.Ar alpha , -is an azimuthal angle in [0,\ 360[ and the second one, -.Ar beta , -is an elevation in [0,\ 90]. +.It Fl A Ar algorithm Ns +The algorithm used to compute sun directions. +Only meaningful if a direction needs to be computed from a location. +Available algorithms are: +.Bl -tag -width Ds +.It Ar psa +The algorithm published by Manuel Blanco et al. in Solar Energy +.Pq see Sy Blanco et al. in the SEE ALSO section . +.It Ar meeus +The algorithm published by J. Meeus in Astronomical Algorithms +.Pq see Sy Meeus in the SEE ALSO section . +.El +.Pp +By default +.Ar algorithm +is set to +.Sy @SOLSTICE_ARGS_DEFAULT_SUN_DIRECTION_ALGORITHM@ . +.It Fl D Ar azimuth,elevation Ns +A sun direction, defined by two angles in degrees. +The first one is the azimuthal angle in [0,\ 360[, and the second one +is the elevation in [0,\ 90]. Each provided sun direction triggers a new computation whose results are concatenated to the -.Ar output -file. +.Ar output . .Pp Following the right-handed convention, azimuthal rotation is -counter-clockwise, with 0\(de on the X axis. +counter-clockwise, with 0\(de on the +X axis. Elevation starts from 0\(de for directions in the XY plane, up to 90\(de at zenith. Thus @@ -134,9 +145,9 @@ and .Fl D Ns Ar 270,0 produce solar vectors {-1,0,0}, {0,-1,0}, {+1,0,0} and {0,+1,0} respectively, while -.Fl D Ns Ar alpha , Ns 90 +.Fl D Ns Ar azimuth , Ns 90 produces {0,0,-1} regardless of the value of -.Ar alpha . +.Ar azimuth . .It Fl f Force overwrite of the output files, i.e.\& the .Ar output @@ -190,6 +201,25 @@ is set to .El .It Fl h List short help and exit. +.It Fl I Ar dni +Set the Direct Normal Irradiance value, in in W/m\u2\s0\d, used for the +computation. +Must be in ]0 INF). +If unset, use the mandatory +.Ar dni +value describing the sun in the input data. +.It Fl L Ar latitude,longitude +Define the location of the solar plant. +The +.Ar latitude +must be in [-90, 90] degrees relative to the equator, counting +positive towards the north. +The +.Ar longitude +must be in [-180, 180] degrees relative to Greenwich, counting +positive towards the east. +A location must be defined before any time is defined. +It is then applied to any following time until a new location is defined. .It Fl n Ar samples-count Number of Monte-Carlo samples used to estimate the solar flux. By default @@ -199,7 +229,7 @@ is set to .It Fl o Ar output Write results to .Ar output -with respect to the +in the .Xr solstice-output 5 format. If not defined, write results to standard output. @@ -263,7 +293,7 @@ the sun is ignored and the only light source is positioned at the camera position. In .Cm pt -mode, the scene is rendered with the un-biased path-tracing Monte-Carlo +mode, the scene is rendered with the unbiased path-tracing Monte-Carlo algorithm; the materials described in the committed .Ar file as well as the submitted sun directions are correctly handled and a @@ -303,8 +333,26 @@ By default, is set to .Sy { Ns @SOLSTICE_ARGS_DEFAULT_CAMERA_UP@ Ns } . .El +.It Fl T Ar utc_time Ns +A time, defined by a string in Coordinated Universal Time +.Pq UTC+00:00 . +.Pp +The date must be in the format +.Dq YYYY-MM-DDThh:mm:ss , +i.e., as printed for the current date by the following date command: +.Bd -literal -offset indent +date -u +"%Y-%m-%dT%H:%M:%S" +.Ed +.Pp +Each provided +.Ar time +is used in conjunction with the last provided location to compute a +sun direction using the selected algorithm +and triggers a new computation whose results are +concatenated to the +.Ar output . .It Fl t Ar threads-count -Hint on the number of threads to use. +Hint at the number of threads to use. By default, as many threads as CPU cores are used. .It Fl v Make @@ -326,16 +374,29 @@ are written to .Pa output even though this file already exists: .Bd -literal -offset indent -solstice -D45,70:50,75 -R rcvs.yaml -n 10000 -f -o output input.yaml +solstice -D45,70 -D50,75 -R rcvs.yaml -n 10000 -f -o output input.yaml +.Ed +.Pp +Launch one simulation at Toulouse, France, at 2 PM on May 1 2022 +.Pq UTC+2 . +The solar facility is described in +.Pa input.yaml +and the receivers on which the integrations must be performed are declared +in +.Pa rcvs.yaml . +The sun direction is computed using the psa algorithm. +The results are written to standard output: +.Bd -literal -offset indent +solstice -L43.605,1.445 -A psa -T"2022-05-01T12:00:00" -R rcvs.yaml input.yaml .Ed .Pp Generate a mesh for each geometry described in .Pa input.yaml and save them in .Pa output -with respect to the Alias Wavefront OBJ format. +in the Alias Wavefront OBJ format. The meshes are positioned according to their orientation constraints, -with respect to the sun direction whose azimuthal and elevation angles +for the sun direction whose azimuthal and elevation angles are {30,60}. Use .Xr csplit 1 @@ -359,7 +420,7 @@ csplit -f geom -b %02d.obj -z --suppress-matched output /^---$/ {*} .Pp Trace 100 radiative paths into the solar plant described in .Pa input.yaml , -with respect to the sun direction whose azimuthal and elevation angles +for the sun direction whose azimuthal and elevation angles are 0 and 90 degrees, respectively. Write the .Xr solstice-output 5 @@ -375,7 +436,7 @@ solstice -n 100 -D0,90 -R rcvs.yaml -p default input.yaml | sed '1d' > paths.vtk .Pp Use the path-tracing rendering algorithm to draw the solar plant .Pa solplant.yaml -with respect to the sun direction whose azimuthal and elevation angles +for the sun direction whose azimuthal and elevation angles are 180 and 45 degrees, respectively. Use 64 samples per pixel to estimate the per-pixel radiance and fix the camera up vector to {0,0,1}. @@ -397,6 +458,23 @@ solstice -D180,45 -r up=0,0,1:rmode=pt:spp=64 solplant.yaml | sed '1d' | feh - .Xr solstice-input 5 , .Xr solstice-output 5 , .Xr solstice-receiver 5 +.Rs +.%A Manuel Blanco-Muriel +.%A Diego C. Alarcón-Padilla +.%A Teodoro López-Moratalla +.%A Martín Lara-Coira +.%T Computing the solar vector +.%J Solar Energy +.%V 70 +.%N 5 +.%D 2001 +.%P 431-441 +.Re +.Rs +.%A Jean Meeus +.%B Astronomical Algorithms +.%D 1991 +.Re .Sh HISTORY .Nm was initially developed with funding from the @@ -406,7 +484,3 @@ 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/src/main.c b/src/main.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/solparser.c b/src/parser/solparser.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/solparser.h b/src/parser/solparser.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/solparser_atmosphere.c b/src/parser/solparser_atmosphere.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/solparser_atmosphere.h b/src/parser/solparser_atmosphere.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/solparser_c.h b/src/parser/solparser_c.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/solparser_entity.c b/src/parser/solparser_entity.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/solparser_entity.h b/src/parser/solparser_entity.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/solparser_geometry.c b/src/parser/solparser_geometry.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/solparser_geometry.h b/src/parser/solparser_geometry.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/solparser_image.c b/src/parser/solparser_image.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/solparser_image.h b/src/parser/solparser_image.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/solparser_material.c b/src/parser/solparser_material.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/solparser_material.h b/src/parser/solparser_material.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/solparser_medium.c b/src/parser/solparser_medium.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/solparser_medium.h b/src/parser/solparser_medium.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/solparser_mtl_data.c b/src/parser/solparser_mtl_data.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/solparser_mtl_data.h b/src/parser/solparser_mtl_data.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/solparser_pivot.c b/src/parser/solparser_pivot.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/solparser_pivot.h b/src/parser/solparser_pivot.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/solparser_shape.h b/src/parser/solparser_shape.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/solparser_spectrum.c b/src/parser/solparser_spectrum.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/solparser_spectrum.h b/src/parser/solparser_spectrum.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/solparser_sun.c b/src/parser/solparser_sun.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/solparser_sun.h b/src/parser/solparser_sun.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/test_solparser.c b/src/parser/test_solparser.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/test_solparser2.c b/src/parser/test_solparser2.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/test_solparser3.c b/src/parser/test_solparser3.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/test_solparser4.c b/src/parser/test_solparser4.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/test_solparser5.c b/src/parser/test_solparser5.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/test_solparser6.c b/src/parser/test_solparser6.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/test_solparser7.c b/src/parser/test_solparser7.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/test_solparser8.c b/src/parser/test_solparser8.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/test_solparser_mirror.c b/src/parser/test_solparser_mirror.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/test_solparser_normal_map.c b/src/parser/test_solparser_normal_map.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/parser/test_solparser_spectrum.c b/src/parser/test_solparser_spectrum.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/receivers/srcvl.c b/src/receivers/srcvl.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/receivers/srcvl.h b/src/receivers/srcvl.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/receivers/test_srcvl.c b/src/receivers/test_srcvl.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/receivers/test_srcvl2.c b/src/receivers/test_srcvl2.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/score.h b/src/score.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/solstice.c b/src/solstice.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify @@ -30,18 +30,8 @@ #include <stdio.h> #include <fcntl.h> -#ifdef COMPILER_CL - /* Wrap POSIX functions and constants */ - #include <io.h> - #define open _open - #define close _close - #define fdopen _fdopen - #define S_IRUSR S_IREAD - #define S_IWUSR S_IWRITE -#else - /* open/close functions */ - #include <unistd.h> -#endif +/* open/close functions */ +#include <unistd.h> #include <solstice/ssol.h> @@ -53,11 +43,7 @@ log_err(const char* msg, void* ctx) { ASSERT(msg); (void)ctx; -#ifdef OS_WINDOWS - fprintf(stderr, "error: %s", msg); -#else fprintf(stderr, "\x1b[31merror:\x1b[0m %s", msg); -#endif } static void @@ -65,11 +51,7 @@ log_warn(const char* msg, void* ctx) { ASSERT(msg); (void)ctx; -#ifdef OS_WINDOWS - fprintf(stderr,"warning: %s", msg); -#else fprintf(stderr, "\x1b[33mwarning:\x1b[0m %s", msg); -#endif } static void @@ -151,7 +133,7 @@ auto_look_at double radius; double depth; res_T res; - ASSERT(scn && fov_x && proj_ratio && up); + ASSERT(scn && fov_x > 0 && proj_ratio > 0 && up); res = ssol_scene_compute_aabb(scn, flower, fupper); if(res != RES_OK) { @@ -316,9 +298,11 @@ error: goto exit; } +/* This function differs from scem_sun_position_to_sun_vector because 1) angles + * are in degrees VS radians, 2) the conventions on azimuth are different. */ static INLINE void spherical_to_cartesian_sun_dir - (struct solstice_args_spherical* spherical, double sun_dir[3]) + (const struct solstice_spherical* spherical, double sun_dir[3]) { double cos_azimuth; double sin_azimuth; @@ -344,38 +328,41 @@ spherical_to_cartesian_sun_dir static res_T setup_sun_dirs(struct solstice* solstice, const struct solstice_args* args) { - double* sun_dirs = NULL; - double* sun_angles = NULL; + struct solstice_sun_dir* sun_dirs = NULL; size_t i; res_T res = RES_OK; ASSERT(solstice && args); - res = darray_double_resize(&solstice->sun_dirs, args->nsun_dirs*3/*#dims*/); + res = darray_sun_dir_resize(&solstice->sun_dirs, args->nsun_dirs); if(res != RES_OK) { fprintf(stderr, "Could not reserve the list of %lu sun directions.\n", (unsigned long)args->nsun_dirs); goto error; } - res = darray_double_resize(&solstice->sun_angles, args->nsun_dirs*2/*#dims*/); - if(res != RES_OK) { - fprintf(stderr, - "Could not reserve the list of %lu sun angles.\n", - (unsigned long)args->nsun_dirs); - goto error; - } - sun_dirs = darray_double_data_get(&solstice->sun_dirs); - sun_angles = darray_double_data_get(&solstice->sun_angles); + sun_dirs = darray_sun_dir_data_get(&solstice->sun_dirs); FOR_EACH(i, 0, args->nsun_dirs) { - spherical_to_cartesian_sun_dir(args->sun_dirs + i, sun_dirs + i*3/*#dims*/); - d2(sun_angles + i*2, args->sun_dirs[i].azimuth, args->sun_dirs[i].elevation); + struct solstice_args_sun_dir* r = args->sun_dirs + i; + switch(r->type) { + case SOLSTICE_ARGS_SPHERICAL: + sun_dirs[i].type = SOLSTICE_SUN_DIR_SPHERICAL; + sun_dirs[i].u.spherical.azimuth = r->spherical.azimuth; + sun_dirs[i].u.spherical.elevation = r->spherical.elevation; + break; + case SOLSTICE_SUN_DIR_LOCATION_TIME: + sun_dirs[i].type = SOLSTICE_SUN_DIR_LOCATION_TIME; + sun_dirs[i].u.location_time.time = r->location_time.time; + sun_dirs[i].u.location_time.latitude = r->location_time.latitude; + sun_dirs[i].u.location_time.longitude = r->location_time.longitude; + break; + default: FATAL("Invalid enum value.\n"); + } } exit: return res; error: - darray_double_clear(&solstice->sun_dirs); - darray_double_clear(&solstice->sun_angles); + darray_sun_dir_clear(&solstice->sun_dirs); goto exit; } @@ -397,13 +384,8 @@ load_data(struct solstice* solstice, const struct solstice_args* args) } name = args->input_filename; } else if(!args->quiet) { -#ifndef OS_WINDOWS - fprintf(stderr, - "Enter the solar facility data. Type ^D (i.e. CTRL+d) to stop:\n"); -#else fprintf(stderr, "Enter the solar facility data. Type ^Z (i.e. CTRL+z) to stop:\n"); -#endif } res = solparser_setup(solstice->parser, name, file); @@ -601,8 +583,7 @@ solstice_init darray_nodes_init(allocator, &solstice->roots); darray_nodes_init(allocator, &solstice->pivots); darray_receiver_init(allocator, &solstice->rcvs_list); - darray_double_init(allocator, &solstice->sun_dirs); - darray_double_init(allocator, &solstice->sun_angles); + darray_sun_dir_init(allocator, &solstice->sun_dirs); solstice->allocator = allocator ? allocator : &mem_default_allocator; @@ -638,6 +619,16 @@ solstice_init res = setup_sun_dirs(solstice, args); if(res != RES_OK) goto error; + switch(args->sun_algorithm) { + case SOLSTICE_SUN_DIRECTION_ALGORITHM_MEEUS: + solstice->sun_algorithm = SSOL_SUN_DIRECTION_ALGORITHM_MEEUS; + break; + case SOLSTICE_SUN_DIRECTION_ALGORITHM_PSA: + solstice->sun_algorithm = SSOL_SUN_DIRECTION_ALGORITHM_PSA; + break; + default: FATAL("Invalid sun algorithm.\n"); break; + } + if(args->rng_state_input_filename) { solstice->rng_state_input = fopen(args->rng_state_input_filename, "r"); if(!solstice->rng_state_input) { @@ -692,6 +683,16 @@ solstice_init solstice->dump_split_mode = args->dump_split_mode; solstice->dump_paths = args->dump_paths; + /* If a dni value is provided on the command line, the dni defined in the yaml + * description of the sun is no longer used */ + if(args->dni > 0) { + res = ssol_sun_set_dni(solstice->sun, args->dni); + if(res != RES_OK) { + fprintf(stderr, "Could not setup the DNI of the sun.\n"); + goto error; + } + } + solstice->path_tracker = SSOL_PATH_TRACKER_DEFAULT; solstice->path_tracker.infinite_ray_length = args->infinite_ray_length; solstice->path_tracker.sun_ray_length = args->sun_ray_length; @@ -741,16 +742,14 @@ solstice_release(struct solstice* solstice) darray_nodes_release(&solstice->roots); darray_nodes_release(&solstice->pivots); darray_receiver_release(&solstice->rcvs_list); - darray_double_release(&solstice->sun_dirs); - darray_double_release(&solstice->sun_angles); + darray_sun_dir_release(&solstice->sun_dirs); logger_release(&solstice->logger); } res_T solstice_run(struct solstice* solstice) { - const double* sun_dirs = NULL; - const double* sun_angles = NULL; + const struct solstice_sun_dir* sun_dirs = NULL; size_t nsun_dirs = 0; size_t i; int dump; @@ -758,11 +757,8 @@ solstice_run(struct solstice* solstice) res_T res = RES_OK; ASSERT(solstice); - sun_dirs = darray_double_cdata_get(&solstice->sun_dirs); - sun_angles = darray_double_cdata_get(&solstice->sun_angles); - nsun_dirs = darray_double_size_get(&solstice->sun_dirs); - ASSERT(nsun_dirs%3 == 0); - nsun_dirs /= 3/*#dims*/; + sun_dirs = darray_sun_dir_cdata_get(&solstice->sun_dirs); + nsun_dirs = darray_sun_dir_size_get(&solstice->sun_dirs); dump = solstice->dump_format != SOLSTICE_ARGS_DUMP_NONE; draw = solstice->framebuffer != NULL; @@ -788,20 +784,45 @@ solstice_run(struct solstice* solstice) } } else { FOR_EACH(i, 0, nsun_dirs) { - const double* sun_dir = sun_dirs + i*3/*#dims*/; - const double* sun_angle = sun_angles + i*2/*#angles*/; - fprintf(solstice->output, "#--- Sun direction: %g %g (%g %g %g)\n", - SPLIT2(sun_angle), SPLIT3(sun_dir)); + double sun_dir[3]; + if(sun_dirs[i].type == SOLSTICE_SUN_DIR_SPHERICAL) { + spherical_to_cartesian_sun_dir(&sun_dirs[i].u.spherical, sun_dir); + fprintf(solstice->output, "#--- Sun direction: %g %g (%g %g %g)\n", + sun_dirs[i].u.spherical.azimuth, sun_dirs[i].u.spherical.elevation, + SPLIT3(sun_dir)); + res = ssol_sun_set_direction(solstice->sun, sun_dir); + if(res != RES_OK) { + fprintf(stderr, "Could not update the sun direction.\n"); + goto error; + } + } else { + char buf[128]; + struct ssol_location loc; + const struct solstice_location_time* lt = &sun_dirs[i].u.location_time; + ASSERT(sun_dirs[i].type == SOLSTICE_SUN_DIR_LOCATION_TIME); + loc.latitude = lt->latitude; + loc.longitude = lt->longitude; + res = ssol_sun_set_location_and_date(solstice->sun, &loc, &lt->time); + if(res != RES_OK) { + fprintf(stderr, "Could not update the sun location and time.\n"); + goto error; + } + res = ssol_sun_get_direction(solstice->sun, sun_dir); + if(res != RES_OK) goto error; + if(0 == strftime(buf, sizeof(buf), + "%Y-%m-%dT%H:%M:%S", &sun_dirs[i].u.location_time.time)) { + res = RES_BAD_ARG; + goto error; + } + fprintf(solstice->output, + "#--- Sun location and time: %g %g %s (%g %g %g)\n", + lt->latitude, lt->longitude, buf, + SPLIT3(sun_dir)); + } res = solstice_update_entities(solstice, sun_dir); if(res != RES_OK) goto error; - res = ssol_sun_set_direction(solstice->sun, sun_dir); - if(res != RES_OK) { - fprintf(stderr, "Could not update the sun direction.\n"); - goto error; - } - if(draw) { res = solstice_draw(solstice); if(res != RES_OK) goto error; diff --git a/src/solstice.h b/src/solstice.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify @@ -17,7 +17,6 @@ #ifndef SOLSTICE_H #define SOLSTICE_H -#include "parser/solparser_material.h" #include "receivers/srcvl.h" #include "solstice_args.h" @@ -161,6 +160,38 @@ struct solstice_primary { #define HTABLE_DATA struct solstice_primary #include <rsys/hash_table.h> +enum solstice_sun_dir_type { + SOLSTICE_SUN_DIR_SPHERICAL, + SOLSTICE_SUN_DIR_LOCATION_TIME +}; + + +enum solstice_sun_direction_algorithm { + SOLSTICE_SUN_DIRECTION_ALGORITHM_MEEUS, + SOLSTICE_SUN_DIRECTION_ALGORITHM_PSA +}; +struct solstice_location_time { + struct tm time; + double latitude, longitude; +}; + +struct solstice_spherical { + double azimuth; /* In radians */ + double elevation; /* In radians */ +}; + +struct solstice_sun_dir { + enum solstice_sun_dir_type type; + union { + struct solstice_spherical spherical; + struct solstice_location_time location_time; + } u; +}; + +#define DARRAY_NAME sun_dir +#define DARRAY_DATA struct solstice_sun_dir +#include <rsys/dynamic_array.h> + struct solstice { struct ssol_device* ssol; struct ssol_scene* scene; @@ -195,8 +226,9 @@ struct solstice { /* Dump radiative paths mode */ struct ssol_path_tracker path_tracker; - struct darray_double sun_dirs; /* List of double3 */ - struct darray_double sun_angles; + /* Sun directions, either a spherical dir or by location+time */ + struct darray_sun_dir sun_dirs; + enum ssol_sun_direction_algorithm sun_algorithm; size_t nexperiments; /* # MC experiments */ FILE* output; /* Output stream */ diff --git a/src/solstice_args.c b/src/solstice_args.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify @@ -14,27 +14,33 @@ * 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 _XOPEN_SOURCE /* strptime support */ #define _POSIX_C_SOURCE 2 #include "solstice_args.h" #include "solstice_version.h" #include <rsys/cstr.h> +#include <rsys/double2.h> #include <rsys/double3.h> #include <rsys/stretchy_array.h> -#ifdef COMPILER_CL - #include <getopt.h> - #define strtok_r strtok_s -#else - #include <unistd.h> -#endif +#include <unistd.h> #include <string.h> +#include <time.h> /******************************************************************************* * Helper functions ******************************************************************************/ +static const char* ALGO_NAMES[] = { "meeus", "psa" }; + +static const char* +algo_to_str(enum solstice_args_sun_direction_algorithm algo) { + ASSERT((unsigned)algo < (sizeof(ALGO_NAMES)/sizeof(*ALGO_NAMES))); + return ALGO_NAMES[algo]; +} + static void print_help(const char* program) { @@ -45,7 +51,10 @@ print_help(const char* program) "man page for more informations.\n\n", program); printf( -" -D <dirs> list of sun directions.\n"); +" -A <algo> set the algorithm to compute sun directions. Default is %s.\n", + algo_to_str(SOLSTICE_ARGS_DEFAULT.sun_algorithm)); + printf( +" -D <dir> request a computation for a sun direction.\n"); printf( " -f overwrite the output files if they already exist, i.e. the\n" " OUTPUT file and the output RNG state.\n"); @@ -56,9 +65,13 @@ print_help(const char* program) printf( " -h display this help and exit.\n"); printf( +" -I <DNI> set the DNI value.\n"); + printf( " -n SAMPLES number of Monte Carlo samples. Default is %lu.\n", SOLSTICE_ARGS_DEFAULT.nexperiments); printf( +" -L <location> set the location of the plant on earth surface.\n"); + printf( " -o OUTPUT write results to OUTPUT. If not defined, write results to\n" " standard output.\n"); printf( @@ -70,6 +83,9 @@ print_help(const char* program) printf( " -r <rendering> switch in rendering mode and configure it.\n"); printf( +" -T <time> request a computation at a given time at the last specified\n" +" location.\n"); + printf( " -t THREADS hint on the number of threads to use. By default use as\n" " many threads as CPU cores.\n"); printf( @@ -78,7 +94,7 @@ print_help(const char* program) " --version display version information and exit.\n"); printf("\n"); printf( -"Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com).\n" +"Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com).\n" "Copyright (C) 2016-2018 CNRS.\n" "Solstice is a free software released under the GNU GPL license, version 3 or\n" "later. You are free to change or redistribute it under certain conditions\n" @@ -139,58 +155,139 @@ error: } static res_T -parse_sun_dir_list(const char* str, struct solstice_args* args) +parse_sun_spherical(const char* str, struct solstice_args* args) { - char buf[512]; - char* tk; - char* ctx; size_t len; + struct solstice_args_sun_dir sun_dir; + double tmp[2]; res_T res = RES_OK; ASSERT(str && args); - if(strlen(str) >= sizeof(buf) - 1/*NULL char*/) { + res = cstr_to_list_double(str, ',', tmp, &len, 2); + if(res != RES_OK || len != 2) { + if(res == RES_OK) res = RES_BAD_ARG; + fprintf(stderr, "Invalid sun direction `%s'.\n", str); + goto error; + } + + if(tmp[0] < 0 || tmp[0] >= 360) { fprintf(stderr, - "Could not duplicate the list of sun directions `%s'.\n", str); - res = RES_MEM_ERR; + "Invalid azimuth angle `%g'. Azimuth must be in [0, 360[ degrees.\n", + tmp[0]); + res = RES_BAD_ARG; + goto error; + } + if(tmp[1] < 0 || tmp[1] > 90) { + fprintf(stderr, + "Invalid elevation angle `%g'. Elevation must be in [0, 90] degrees.\n", + tmp[1]); + res = RES_BAD_ARG; goto error; } - strncpy(buf, str, sizeof(buf)); - tk = strtok_r(buf, ":", &ctx); - while(tk) { - struct solstice_args_spherical spherical; - double tmp[2]; + sun_dir.type = SOLSTICE_ARGS_SPHERICAL; + sun_dir.spherical.azimuth = tmp[0]; + sun_dir.spherical.elevation = tmp[1]; + sa_push(args->sun_dirs, sun_dir); + args->nsun_dirs++; - res = cstr_to_list_double(tk, ',', tmp, &len, 2); - if(res == RES_OK && len != 2) res = RES_BAD_ARG; - if(res != RES_OK) { - fprintf(stderr, "Invalid sun direction `%s'.\n", tk); - goto error; - } +exit: + return res; +error: + if(args->sun_dirs) { + sa_release(args->sun_dirs); + args->sun_dirs = NULL; + args->nsun_dirs = 0; + } + goto exit; +} - if(tmp[0] < 0 || tmp[0] >= 360) { - fprintf(stderr, - "Invalid azimuth angle `%g'. Azimuth must be in [0, 360[ degrees.\n", +static res_T +parse_sun_algorithm(const char* str, struct solstice_args* args) +{ + res_T res = RES_OK; + ASSERT(str && args); + + if(0 == strcmp(str, "meeus")) { + args->sun_algorithm = SOLSTICE_ARGS_SUN_DIRECTION_ALGORITHM_meeus; + } + else if(0 == strcmp(str, "psa")) { + args->sun_algorithm = SOLSTICE_ARGS_SUN_DIRECTION_ALGORITHM_psa; + } + else { + res = RES_BAD_ARG; + fprintf(stderr, "Invalid sun algorithm `%s'.\n", str); + goto error; + } + +exit: + return res; +error: + goto exit; +} + +static res_T +parse_location + (const char* str, + double current_loc[2]) +{ + size_t len; + double tmp[2]; + res_T res = RES_OK; + ASSERT(str && current_loc); + + res = cstr_to_list_double(str, ',', tmp, &len, 2); + if(res != RES_OK || len != 2) { + if(res == RES_OK) res = RES_BAD_ARG; + fprintf(stderr, "Invalid location: `%s'.\n", str); + goto error; + } + + if(tmp[0] < -90 || tmp[0] > 90) { + fprintf(stderr, + "Invalid latitude `%g'. Must be in [-90, 90] degrees.\n", tmp[0]); - res = RES_BAD_ARG; - goto error; - } - if(tmp[1] < 0 || tmp[1] > 90) { - fprintf(stderr, - "Invalid elevation angle `%g'. Elevation must be in [0, 90] degrees.\n", + res = RES_BAD_ARG; + goto error; + } + if(tmp[1] < -180 || tmp[1] > 180) { + fprintf(stderr, + "Invalid longitude `%g'. Must be in [-180, 180] degrees.\n", tmp[1]); - res = RES_BAD_ARG; - goto error; - } + res = RES_BAD_ARG; + goto error; + } - spherical.azimuth = tmp[0]; - spherical.elevation = tmp[1]; - sa_push(args->sun_dirs, spherical); + d2_set(current_loc, tmp); +exit: + return res; +error: + goto exit; +} - tk = strtok_r(NULL, ":", &ctx); +static res_T +parse_time + (const char* str, + const double current_loc[2], + struct solstice_args* args) +{ + res_T res = RES_OK; + struct solstice_args_sun_dir sun_dir; + char* p; + ASSERT(str && current_loc && args); + + p = strptime(str, "%Y-%m-%dT%H:%M:%S", &sun_dir.location_time.time); + if(p == NULL || *p != '\0') { + res = RES_BAD_ARG; + fprintf(stderr, "Invalid time `%s'.\n", str); + goto error; } - args->nsun_dirs += sa_size(args->sun_dirs); + sun_dir.type = SOLSTICE_ARGS_LOCATION_TIME; + sun_dir.location_time.latitude = current_loc[0]; + sun_dir.location_time.longitude = current_loc[1]; + sa_push(args->sun_dirs, sun_dir); + args->nsun_dirs++; exit: return res; @@ -545,7 +642,8 @@ res_T solstice_args_init(struct solstice_args* args, const int argc, char** argv) { int opt; - int i; + int i, L_defined = 0; + double current_loc[2]; res_T res = RES_OK; ASSERT(args && argc && argv); @@ -563,21 +661,15 @@ solstice_args_init(struct solstice_args* args, const int argc, char** argv) } optind = 0; - while((opt = getopt(argc, argv, "D:fG:g:hn:o:p:qR:r:t:v")) != -1) { + while((opt = getopt(argc, argv, "A:D:fG:g:hI:n:L:o:p:qR:r:T:t:v")) != -1) { switch(opt) { - case 'D': /* Sun directions */ - res = parse_sun_dir_list(optarg, args); + case 'A': /* 1 sun direction */ + res = parse_sun_algorithm(optarg, args); break; - case 'f': args->force_overwriting = 1; break; - case 'h': /* Print short help and exit */ - print_help(argv[0]); - solstice_args_release(args); - args->quit = 1; - goto exit; - case 'n': /* Define the number of experiments */ - res = cstr_to_ulong(optarg, &args->nexperiments); - if(res == RES_OK && !args->nexperiments) res = RES_BAD_ARG; + case 'D': /* 1 sun direction */ + res = parse_sun_spherical(optarg, args); break; + case 'f': args->force_overwriting = 1; break; case 'G': /* Setup the random number generator */ res = parse_multiple_options(optarg, args, parse_rng_option); break; @@ -587,8 +679,31 @@ solstice_args_init(struct solstice_args* args, const int argc, char** argv) fprintf(stderr, "%s: missing a dump format -- `%s'.\n", argv[0], optarg); res = RES_BAD_ARG; + goto error; } break; + case 'h': /* Print short help and exit */ + print_help(argv[0]); + solstice_args_release(args); + args->quit = 1; + goto exit; + case 'I': /* DNI */ + res = cstr_to_double(optarg, &args->dni); + if(res == RES_OK && args->dni <= 0) { + fprintf(stderr, "%s: invalid DNI value -- `%s'.\n", + argv[0], optarg); + res = RES_BAD_ARG; + goto error; + } + break; + case 'L': /* Plant location */ + L_defined = 1; + res = parse_location(optarg, current_loc); + break; + case 'n': /* Define the number of MC samples */ + res = cstr_to_ulong(optarg, &args->nexperiments); + if(res == RES_OK && !args->nexperiments) res = RES_BAD_ARG; + break; case 'o': args->output_filename = optarg; break; case 'p': /* Switch in dump radiative paths mode and configure it */ args->dump_paths = 1; @@ -600,6 +715,17 @@ solstice_args_init(struct solstice_args* args, const int argc, char** argv) args->rendering = 1; res = parse_multiple_options(optarg, args, parse_rendering_option); break; + case 'T': /* 1 time */ + if(!L_defined) { + fprintf(stderr, + "%s: cannot use the -%c option with no location defined '%s'\n", + argv[0], opt, optarg); + res = RES_BAD_ARG; + goto error; + break; + } + res = parse_time(optarg, current_loc, args); + break; case 't': /* Submit an hint on the number of threads to use */ res = cstr_to_uint(optarg, &args->nthreads); if(res == RES_OK && !args->nthreads) res = RES_BAD_ARG; @@ -610,16 +736,17 @@ solstice_args_init(struct solstice_args* args, const int argc, char** argv) if(res != RES_OK) { if(optarg) { fprintf(stderr, "%s: invalid option argument '%s' -- '%c'\n", - argv[0], optarg, opt); + argv[0], optarg, opt); } goto error; } } if(!args->rendering - && args->dump_format == SOLSTICE_ARGS_DUMP_NONE - && !args->nsun_dirs) { - fprintf(stderr, "Missing sun direction.\n"); + && args->dump_format == SOLSTICE_ARGS_DUMP_NONE + && args->nsun_dirs == 0) + { + fprintf(stderr, "No computation request.\n"); res = RES_BAD_ARG; goto error; } diff --git a/src/solstice_args.h.in b/src/solstice_args.h.in @@ -21,10 +21,39 @@ #include <solstice/ssol.h> #include <rsys/math.h> +#include <time.h> + +struct solstice_args_location { + double longiture; + double latitude; +}; + +#define SOLSTICE_ARGS_LOCATION_NONE { 0, 0 } + +enum solstice_args_sun_dir_type { + SOLSTICE_ARGS_SPHERICAL, + SOLSTICE_ARGS_LOCATION_TIME +}; + +enum solstice_args_sun_direction_algorithm { + SOLSTICE_ARGS_SUN_DIRECTION_ALGORITHM_meeus, + SOLSTICE_ARGS_SUN_DIRECTION_ALGORITHM_psa +}; struct solstice_args_spherical { - double azimuth; /* In radians */ - double elevation; /* In radians */ + double azimuth; + double elevation; +}; + +struct solstice_args_location_time { + struct tm time; + double latitude, longitude; +}; + +struct solstice_args_sun_dir { + enum solstice_args_sun_dir_type type; + struct solstice_args_location_time location_time; + struct solstice_args_spherical spherical; }; enum solstice_args_dump_format { @@ -47,11 +76,13 @@ struct solstice_args { const char* output_filename; const char* input_filename; /* May be NULL <=> read data from stdin */ const char* receivers_filename; + double dni; unsigned long nexperiments; /* #experiments */ unsigned nthreads; /* #threads */ + enum solstice_args_sun_direction_algorithm sun_algorithm; /* List of sun directions */ - struct solstice_args_spherical* sun_dirs; + struct solstice_args_sun_dir* sun_dirs; size_t nsun_dirs; struct { @@ -97,8 +128,10 @@ static const struct solstice_args SOLSTICE_ARGS_NULL = SOLSTICE_ARGS_NULL__; NULL, /* output_filename */ \ NULL, /* input_filename */ \ NULL, /* receivers_filename */ \ + -1, \ @SOLSTICE_ARGS_DEFAULT_NREALISATIONS@, \ SSOL_NTHREADS_DEFAULT, \ + @SOLSTICE_ARGS_DEFAULT_SUN_DIRECTION_ALGORITHM@, \ \ NULL, /* sun_dirs */ \ 0, /* # nsun_dirs */ \ diff --git a/src/solstice_atmosphere.c b/src/solstice_atmosphere.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/solstice_c.h b/src/solstice_c.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/solstice_draw.c b/src/solstice_draw.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/solstice_dump.c b/src/solstice_dump.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/solstice_entity.c b/src/solstice_entity.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/solstice_material.c b/src/solstice_material.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/solstice_node.c b/src/solstice_node.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/solstice_object.c b/src/solstice_object.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/solstice_solve.c b/src/solstice_solve.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/solstice_spectrum.c b/src/solstice_spectrum.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/solstice_sun.c b/src/solstice_sun.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify @@ -43,7 +43,7 @@ create_sun_buie res = ssol_sun_set_buie_param(sun, solparser_sun->radang_distrib.buie.csr); if(res != RES_OK) { - fprintf(stderr, "Could setup the buie parameter of the solver sun.\n"); + fprintf(stderr, "Could not setup the buie parameter of the solver sun.\n"); goto error; } @@ -245,6 +245,12 @@ solstice_create_sun(struct solstice* solstice) } if(res != RES_OK) goto error; + res = ssol_sun_set_algorithm(sun, solstice->sun_algorithm); + if(res != RES_OK) { + fprintf(stderr, "Could not setup the sun direction algorithm.\n"); + goto error; + } + if(!SOLPARSER_ID_IS_VALID(solparser_sun->spectrum)) { res = create_default_sun_spectrum(solstice, solparser_sun, &spectrum); if(res != RES_OK) goto error; diff --git a/src/solstice_sun_spectrum.c b/src/solstice_sun_spectrum.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/solstice_sun_spectrum.h b/src/solstice_sun_spectrum.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/src/test_solstice_args.c b/src/test_solstice_args.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify @@ -14,8 +14,9 @@ * 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 _XOPEN_SOURCE /* strptime support */ + #include "solstice_args.h" -#include "test_solstice_utils.h" #include <rsys/double3.h> #include <rsys/stretchy_array.h> @@ -23,11 +24,7 @@ #include <stdarg.h> #include <string.h> #include <limits.h> - -#ifdef COMPILER_CL - #pragma warning(push) - #pragma warning(disable:4706) /* Assignment within a condition */ -#endif +#include <time.h> static char** cmd_create(int dummy, ...) @@ -55,10 +52,6 @@ cmd_create(int dummy, ...) return cmd; } -#ifdef COMPILER_CL - #pragma warning(pop) -#endif - static void cmd_delete(char** cmd) { @@ -235,79 +228,205 @@ test_rendering(void) cmd_delete(cmd); } +static int +tm_equal(const struct tm* tm1, const struct tm* tm2) { + if(tm1->tm_year != tm2->tm_year) return 0; + if(tm1->tm_mon != tm2->tm_mon) return 0; + if(tm1->tm_mday != tm2->tm_mday) return 0; + if(tm1->tm_hour != tm2->tm_hour) return 0; + if(tm1->tm_min != tm2->tm_min) return 0; + if(tm1->tm_sec != tm2->tm_sec) return 0; + return 1; +} + static void test_sun_dirs(void) { + struct tm ref; struct solstice_args args = SOLSTICE_ARGS_NULL; + char *p; + char timeOK[] = "2020-10-20T10:10:10"; + char* dirsBAD[] = { + "1.2,3.4,5", + "1.2,3.4:1", + "1.2,3.4:5.2,A", + "-0.1,2", + "360,2", + "0,-1", + "0,91", + NULL + }; + char* locsBAD[] = { + "-100,0", + "100,0", + "0,-200", + "0,200", + ",0", + "0,", + "0;0", + "0,0,0", + "A,0", + "0,A", + NULL + }; + char* timeBAD[] = { + "2020-10-20t10:10:10", + "2020_10-20T10:10:10", + "2020-10_20T10:10:10", + "2020-10-20T10;10:10", + "2020-10-20T10:10;10", + "2020-19-20T10:10:10", + "2020-10-20T10:10:100", + "2020-10T10:10:10", + "2020-10-20T10:10", + "2020-A-20T10:10:10", + "2020-10-20T10:10:A", + "2020-10-20-5T10:10:10", + "2020-10-20T10:10:10:10", + "2020--20T10:10:10", + "2020-10-20T10::10", + "-10-20T10:10:10", + "2020-10-20T:10:10", + "2020-10-T10:10:10", + "2020-10-20T10:10:", + NULL + }; + size_t i; char** cmd = NULL; + p = strptime(timeOK, "%Y-%m-%dT%H:%M:%S", &ref); + CHK(p != NULL && *p == '\0'); + cmd = cmd_create(0, "test", "-D", "0,1", NULL); CHK(solstice_args_init(&args, cmd_size(cmd), cmd) == RES_OK); CHK(args.nsun_dirs == 1); - CHK(args.sun_dirs[0].azimuth == 0); - CHK(eq_eps(args.sun_dirs[0].elevation, 1, 1.e-6) == 1); + CHK(args.sun_dirs[0].type == SOLSTICE_ARGS_SPHERICAL); + CHK(args.sun_dirs[0].spherical.azimuth == 0); + CHK(eq_eps(args.sun_dirs[0].spherical.elevation, 1, 1.e-6) == 1); solstice_args_release(&args); cmd_delete(cmd); - cmd = cmd_create(0, "test", "-D", "1.2,3.4:3.14,0.123:", RES_OK); + cmd = cmd_create(0, "test", "-D", "1.2,3.4", "-D", "3.14,0.123", NULL); CHK(solstice_args_init(&args, cmd_size(cmd), cmd) == RES_OK); CHK(args.nsun_dirs == 2); - CHK(eq_eps(args.sun_dirs[0].azimuth, 1.2, 1.e-6) == 1); - CHK(eq_eps(args.sun_dirs[0].elevation, 3.4, 1.e-6) == 1); - CHK(eq_eps(args.sun_dirs[1].azimuth, 3.14, 1.e-6) == 1); - CHK(eq_eps(args.sun_dirs[1].elevation, 0.123, 1.e-6) == 1); + + CHK(args.sun_dirs[0].type == SOLSTICE_ARGS_SPHERICAL); + CHK(eq_eps(args.sun_dirs[0].spherical.azimuth, 1.2, 1.e-6) == 1); + CHK(eq_eps(args.sun_dirs[0].spherical.elevation, 3.4, 1.e-6) == 1); + CHK(args.sun_dirs[1].type == SOLSTICE_ARGS_SPHERICAL); + CHK(eq_eps(args.sun_dirs[1].spherical.azimuth, 3.14, 1.e-6) == 1); + CHK(eq_eps(args.sun_dirs[1].spherical.elevation, 0.123, 1.e-6) == 1); solstice_args_release(&args); cmd_delete(cmd); - cmd = cmd_create(0, "test", "-D", "1.2,3.4:3.14,0.123:2.01,23.1", RES_OK); + cmd = cmd_create(0, "test", "-L", "1.2,3.4", "-T", timeOK, NULL); CHK(solstice_args_init(&args, cmd_size(cmd), cmd) == RES_OK); - CHK(args.nsun_dirs == 3); - CHK(eq_eps(args.sun_dirs[0].azimuth, 1.2, 1.e-6) == 1); - CHK(eq_eps(args.sun_dirs[0].elevation, 3.4, 1.e-6) == 1); - CHK(eq_eps(args.sun_dirs[1].azimuth, 3.14, 1.e-6) == 1); - CHK(eq_eps(args.sun_dirs[1].elevation, 0.123, 1.e-6) == 1); - CHK(eq_eps(args.sun_dirs[2].azimuth, 2.01, 1.e-6) == 1); - CHK(eq_eps(args.sun_dirs[2].elevation, 23.1, 1.e-6) == 1); + CHK(args.nsun_dirs == 1); + + CHK(args.sun_dirs[0].type == SOLSTICE_ARGS_LOCATION_TIME); + CHK(eq_eps(args.sun_dirs[0].location_time.latitude, 1.2, 1.e-6) == 1); + CHK(eq_eps(args.sun_dirs[0].location_time.longitude, 3.4, 1.e-6) == 1); + CHK(tm_equal(&args.sun_dirs[0].location_time.time, &ref) == 1); solstice_args_release(&args); cmd_delete(cmd); - cmd = cmd_create(0, "test", "-D", "1.2,3.4,5", NULL); - CHK(solstice_args_init(&args, cmd_size(cmd), cmd) == RES_BAD_ARG); - cmd_delete(cmd); + cmd = cmd_create(0, "test", "-L", "1,4", "-L", "1.2,3.4", "-T", timeOK, NULL); + CHK(solstice_args_init(&args, cmd_size(cmd), cmd) == RES_OK); + CHK(args.nsun_dirs == 1); - cmd = cmd_create(0, "test", "-D", "1.2,3.4:1", NULL); - CHK(solstice_args_init(&args, cmd_size(cmd), cmd) == RES_BAD_ARG); + CHK(args.sun_dirs[0].type == SOLSTICE_ARGS_LOCATION_TIME); + CHK(eq_eps(args.sun_dirs[0].location_time.latitude, 1.2, 1.e-6) == 1); + CHK(eq_eps(args.sun_dirs[0].location_time.longitude, 3.4, 1.e-6) == 1); + CHK(tm_equal(&args.sun_dirs[0].location_time.time, &ref) == 1); + solstice_args_release(&args); cmd_delete(cmd); - cmd = cmd_create(0, "test", "-D", "1.2,3.4:5.2,A", NULL); - CHK(solstice_args_init(&args, cmd_size(cmd), cmd) == RES_BAD_ARG); - cmd_delete(cmd); + cmd = cmd_create(0, "test", "-L", "1.2,3.4", "-T", timeOK, "-T", timeOK, NULL); + CHK(solstice_args_init(&args, cmd_size(cmd), cmd) == RES_OK); + CHK(args.nsun_dirs == 2); - cmd = cmd_create(0, "test", "-D", "-0.1,2", NULL); - CHK(solstice_args_init(&args, cmd_size(cmd), cmd) == RES_BAD_ARG); + CHK(args.sun_dirs[0].type == SOLSTICE_ARGS_LOCATION_TIME); + CHK(eq_eps(args.sun_dirs[0].location_time.latitude, 1.2, 1.e-6) == 1); + CHK(eq_eps(args.sun_dirs[0].location_time.longitude, 3.4, 1.e-6) == 1); + CHK(tm_equal(&args.sun_dirs[0].location_time.time, &ref) == 1); + CHK(args.sun_dirs[1].type == SOLSTICE_ARGS_LOCATION_TIME); + CHK(eq_eps(args.sun_dirs[1].location_time.latitude, 1.2, 1.e-6) == 1); + CHK(eq_eps(args.sun_dirs[1].location_time.longitude, 3.4, 1.e-6) == 1); + CHK(tm_equal(&args.sun_dirs[1].location_time.time, &ref) == 1); + solstice_args_release(&args); cmd_delete(cmd); - cmd = cmd_create(0, "test", "-D", "360,2", NULL); - CHK(solstice_args_init(&args, cmd_size(cmd), cmd) == RES_BAD_ARG); + cmd = cmd_create(0, "test", "-L", "1,3", "-T", timeOK, "-L", "2,4", "-T", timeOK, NULL); + CHK(solstice_args_init(&args, cmd_size(cmd), cmd) == RES_OK); + CHK(args.nsun_dirs == 2); + + CHK(args.sun_dirs[0].type == SOLSTICE_ARGS_LOCATION_TIME); + CHK(eq_eps(args.sun_dirs[0].location_time.latitude, 1, 1.e-6) == 1); + CHK(eq_eps(args.sun_dirs[0].location_time.longitude, 3, 1.e-6) == 1); + CHK(tm_equal(&args.sun_dirs[0].location_time.time, &ref) == 1); + CHK(args.sun_dirs[1].type == SOLSTICE_ARGS_LOCATION_TIME); + CHK(eq_eps(args.sun_dirs[1].location_time.latitude, 2, 1.e-6) == 1); + CHK(eq_eps(args.sun_dirs[1].location_time.longitude, 4, 1.e-6) == 1); + CHK(tm_equal(&args.sun_dirs[1].location_time.time, &ref) == 1); + solstice_args_release(&args); cmd_delete(cmd); - cmd = cmd_create(0, "test", "-D", "0,-1", NULL); - CHK(solstice_args_init(&args, cmd_size(cmd), cmd) == RES_BAD_ARG); + cmd = cmd_create(0, "test", "-D", "1.2,3.4", "-L", "1.2,3.4", "-T", timeOK, NULL); + CHK(solstice_args_init(&args, cmd_size(cmd), cmd) == RES_OK); + CHK(args.nsun_dirs == 2); + + CHK(args.sun_dirs[0].type == SOLSTICE_ARGS_SPHERICAL); + CHK(eq_eps(args.sun_dirs[0].spherical.azimuth, 1.2, 1.e-6) == 1); + CHK(eq_eps(args.sun_dirs[0].spherical.elevation, 3.4, 1.e-6) == 1); + CHK(args.sun_dirs[1].type == SOLSTICE_ARGS_LOCATION_TIME); + CHK(eq_eps(args.sun_dirs[1].location_time.latitude, 1.2, 1.e-6) == 1); + CHK(eq_eps(args.sun_dirs[1].location_time.longitude, 3.4, 1.e-6) == 1); + CHK(tm_equal(&args.sun_dirs[1].location_time.time, &ref) == 1); + solstice_args_release(&args); cmd_delete(cmd); - cmd = cmd_create(0, "test", "-D", "0,91", NULL); - CHK(solstice_args_init(&args, cmd_size(cmd), cmd) == RES_BAD_ARG); + cmd = cmd_create(0, "test", "-D", "1.2,3.4", "-D", "3.14,0.123", "-D", "2.01,23.1", NULL); + CHK(solstice_args_init(&args, cmd_size(cmd), cmd) == RES_OK); + CHK(args.nsun_dirs == 3); + + CHK(args.sun_dirs[0].type == SOLSTICE_ARGS_SPHERICAL); + CHK(eq_eps(args.sun_dirs[0].spherical.azimuth, 1.2, 1.e-6) == 1); + CHK(eq_eps(args.sun_dirs[0].spherical.elevation, 3.4, 1.e-6) == 1); + CHK(args.sun_dirs[1].type == SOLSTICE_ARGS_SPHERICAL); + CHK(eq_eps(args.sun_dirs[1].spherical.azimuth, 3.14, 1.e-6) == 1); + CHK(eq_eps(args.sun_dirs[1].spherical.elevation, 0.123, 1.e-6) == 1); + CHK(args.sun_dirs[2].type == SOLSTICE_ARGS_SPHERICAL); + CHK(eq_eps(args.sun_dirs[2].spherical.azimuth, 2.01, 1.e-6) == 1); + CHK(eq_eps(args.sun_dirs[2].spherical.elevation, 23.1, 1.e-6) == 1); + solstice_args_release(&args); cmd_delete(cmd); - cmd = cmd_create(0, "test", "-D", NULL); + cmd = cmd_create(0, "test", "-T", timeOK, "-L", "1.2,3.4", NULL); CHK(solstice_args_init(&args, cmd_size(cmd), cmd) == RES_BAD_ARG); cmd_delete(cmd); + for(i = 0; i < sizeof(dirsBAD)/sizeof(*dirsBAD); i++) { + cmd = cmd_create(0, "test", "-D", dirsBAD[i], NULL); + CHK(solstice_args_init(&args, cmd_size(cmd), cmd) == RES_BAD_ARG); + cmd_delete(cmd); + } + + for(i = 0; i < sizeof(locsBAD)/sizeof(*locsBAD); i++) { + cmd = cmd_create(0, "test", "-L", locsBAD[i], NULL); + CHK(solstice_args_init(&args, cmd_size(cmd), cmd) == RES_BAD_ARG); + cmd_delete(cmd); + } + + for(i = 0; i < sizeof(timeBAD)/sizeof(*timeBAD); i++) { + cmd = cmd_create(0, "test", "-L", "1.2,3.4", "-T", timeBAD[i], NULL); + CHK(solstice_args_init(&args, cmd_size(cmd), cmd) == RES_BAD_ARG); + cmd_delete(cmd); + } + cmd = cmd_create(0, "test", "-D", "0,90", NULL); CHK(solstice_args_init(&args, cmd_size(cmd), cmd) == RES_OK); solstice_args_release(&args); cmd_delete(cmd); - } static void @@ -388,7 +507,6 @@ test_threads_count(void) cmd = cmd_create(0, "test", "-D", "0,90", "-t", NULL); CHK(solstice_args_init(&args, cmd_size(cmd), cmd) == RES_BAD_ARG); cmd_delete(cmd); - } static void @@ -726,6 +844,34 @@ test_rng(void) cmd_delete(cmd); } +static void +test_dni(void) +{ + struct solstice_args args = SOLSTICE_ARGS_NULL; + char** cmd = NULL; + + cmd = cmd_create(0, "test", "-D", "0,90", NULL); + CHK(solstice_args_init(&args, cmd_size(cmd), cmd) == RES_OK); + CHK(args.dni == -1); + solstice_args_release(&args); + cmd_delete(cmd); + + cmd = cmd_create(0, "test", "-D", "0,90", "-I", "1", NULL); + CHK(solstice_args_init(&args, cmd_size(cmd), cmd) == RES_OK); + CHK(args.dni == 1); + solstice_args_release(&args); + cmd_delete(cmd); + + cmd = cmd_create(0, "test", "-D", "0,90", "-I", "0", NULL); + CHK(solstice_args_init(&args, cmd_size(cmd), cmd) == RES_BAD_ARG); + solstice_args_release(&args); + cmd_delete(cmd); + + cmd = cmd_create(0, "test", "-D", "0,90", "-I", NULL); + CHK(solstice_args_init(&args, cmd_size(cmd), cmd) == RES_BAD_ARG); + cmd_delete(cmd); +} + int main(int argc, char** argv) { @@ -742,6 +888,7 @@ main(int argc, char** argv) test_dump(); test_dump_paths(); test_rng(); + test_dni(); CHK(mem_allocated_size() == 0); return 0; } diff --git a/src/test_solstice_simulation.c b/src/test_solstice_simulation.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify @@ -20,20 +20,10 @@ #include <rsys/math.h> #include <rsys/double2.h> -#include <math.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#ifdef COMPILER_CL - /* Wrap POSIX functions and constants */ - #include <io.h> - #include <fcntl.h> - #include <sys/stat.h> - #define fdopen _fdopen - #define open _open -#endif - enum side { FRONT, BACK @@ -93,108 +83,6 @@ sundir_header [] = "#--- Sun direction:"; #define IS_NEW_BLOCK(Line, Header) (!strncmp((Line), (Header), strlen(Header))) - -#ifdef COMPILER_CL -/* mkstemp extracted from libc/sysdeps/posix/tempname.c. Copyright - * (C) 1991-1999, 2000, 2001, 2006 Free Software Foundation, Inc. - * - * The GNU C Library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. */ - -static const char letters [] = -"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - -/* Generate a temporary file name based on TMPL. TMPL must match the - * rules for mk[s]temp (i.e. end in "XXXXXX"). The name constructed - * does not exist at the time of the call to mkstemp. TMPL is - * overwritten with the result. */ -int -mkstemp(char *tmpl) -{ - size_t len; - char *XXXXXX; - static unsigned long long value; - unsigned long long random_time_bits; - unsigned int count; - int fd = -1; - int save_errno = errno; - - /* A lower bound on the number of temporary files to attempt to - * generate. The maximum total number of temporary file names that - * can exist for a given template is 62**6. It should never be - * necessary to try all these combinations. Instead if a reasonable - * number of names is tried (we define reasonable as 62**3) fail to - * give the system administrator the chance to remove the problems. */ - #define ATTEMPTS_MIN (62 * 62 * 62) - - /* The number of times to attempt to generate a temporary file. To - * conform to POSIX, this must be no smaller than TMP_MAX. */ - #if ATTEMPTS_MIN < TMP_MAX - unsigned int attempts = TMP_MAX; - #else - unsigned int attempts = ATTEMPTS_MIN; - #endif - - len = strlen(tmpl); - if (len < 6 || strcmp(&tmpl[len - 6], "XXXXXX")) { - errno = EINVAL; - return -1; - } - - /* This is where the Xs start. */ - XXXXXX = &tmpl[len - 6]; - - /* Get some more or less random data. */ - { - SYSTEMTIME stNow; - FILETIME ftNow; - - /* get system time */ - GetSystemTime(&stNow); - stNow.wMilliseconds = 500; - if (!SystemTimeToFileTime(&stNow, &ftNow)) { - errno = -1; - return -1; - } - - random_time_bits = (((unsigned long long)ftNow.dwHighDateTime << 32) - | (unsigned long long)ftNow.dwLowDateTime); - } - value += random_time_bits ^ (unsigned long long)GetCurrentThreadId(); - - for(count = 0; count < attempts; value += 7777, ++count) { - unsigned long long v = value; - - /* Fill in the random bits. */ - XXXXXX[0] = letters[v % 62]; - v /= 62; - XXXXXX[1] = letters[v % 62]; - v /= 62; - XXXXXX[2] = letters[v % 62]; - v /= 62; - XXXXXX[3] = letters[v % 62]; - v /= 62; - XXXXXX[4] = letters[v % 62]; - v /= 62; - XXXXXX[5] = letters[v % 62]; - - fd = open(tmpl, O_RDWR|O_CREAT|O_EXCL, S_IREAD|S_IWRITE); - if (fd >= 0) { - errno = save_errno; - return fd; - } - else if (errno != EEXIST) - return -1; - } - - /* We got out of the loop because we ran out of combinations to try. */ - errno = EEXIST; - return -1; -} -#endif - static int read_line(char* line, size_t max_line_len, FILE* stream) { diff --git a/src/test_solstice_simulation_time.c b/src/test_solstice_simulation_time.c @@ -0,0 +1,501 @@ +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) + * Copyright (C) 2016-2018 CNRS + * + * 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 _XOPEN_SOURCE /* strptime support */ +#define _POSIX_C_SOURCE 200809L /* mkstemp support */ + +#include <star/scem.h> + +#include <rsys/rsys.h> +#include <rsys/math.h> +#include <rsys/double2.h> + +#include <time.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +enum side { + FRONT, + BACK +}; + +enum global_result_type { + GLOBAL_POTENTIAL, + GLOBAL_ABSORBED, + GLOBAL_COS, + GLOBAL_SHADOW, + GLOBAL_MISSING, + GLOBAL_ATMOSPHERE, + GLOBAL_REFLECTIVITY, + GLOBAL_RESULTS_COUNT__ +}; + +enum receiver_result_type { + FIRST_RECEIVER_RESULT, + FRONT_ABSORBED_FLUX = FIRST_RECEIVER_RESULT, + FRONT_INCOMING_FLUX, + FRONT_ABSORBED_FIELD_GAIN, + FRONT_ABSORBED_ATM_GAIN, + FRONT_EFFICIENCY, + BACK_ABSORBED_FLUX, + BACK_INCOMING_FLUX, + BACK_ABSORBED_FIELD_GAIN, + BACK_ABSORBED_ATM_GAIN, + BACK_EFFICIENCY, + RECEIVER_RESULTS_COUNT__ +}; + +enum primary_result_type { + FIRST_PRIMARY_RESULT, + PRIMARY_COS = FIRST_PRIMARY_RESULT, + PRIMARY_SHADOW, + PRIMARY_RESULTS_COUNT__ +}; + +struct counts { + unsigned long global, receiver, primary, realisation, failed; +}; + +static int +counts_ok(const struct counts* ref, const struct counts* c) +{ + CHK(ref->global == GLOBAL_RESULTS_COUNT__); + CHK(c->global == GLOBAL_RESULTS_COUNT__); + return ref->receiver == c->receiver + && ref->primary == c->primary + && ref->failed >= c->failed; +} + +#define MAX_LINE_LEN 2048 + +static const char +sundir_header [] = "#--- Sun direction:"; +static const char +suntime_header [] = "#--- Sun location and time:"; + +#define IS_NEW_BLOCK(Line, Header) (!strncmp((Line), (Header), strlen(Header))) + +static int +read_line(char* line, size_t max_line_len, FILE* stream) +{ + ASSERT(stream && line && max_line_len); + line = fgets(line, (int)max_line_len, stream); + if(!line) return 0; + CHK(strlen(line) + 1 < max_line_len); + return 1; +} + +static int +get_angles_and_counts + (FILE* file, + double angles[2], + struct counts* counts) +{ + char line[MAX_LINE_LEN]; + int r; + + CHK(file != NULL); + CHK(angles != NULL); + CHK(counts != NULL); + + /* Get sun dir */ + r = read_line(line, sizeof(line), file); + if (!r) { + CHK(feof(file) == 1); + return 0; + } + CHK(IS_NEW_BLOCK(line, sundir_header) == 1); + CHK(sscanf(line+strlen(sundir_header), "%lg%lg", &angles[0], &angles[1]) == 2); + + /* Get counts */ + CHK(read_line(line, sizeof(line), file) == 1); + CHK( + sscanf(line, + "%lu %lu %lu %lu %lu", + &counts->global, &counts->receiver, &counts->primary, + &counts->realisation, &counts->failed) == 5); + return 1; +} + +static int +get_dir_and_counts + (FILE* file, + double dir[3], + struct counts* counts) +{ + char line[MAX_LINE_LEN]; + int r, n; + + CHK(file != NULL); + CHK(dir != NULL); + CHK(counts != NULL); + + /* Get sun dir */ + r = read_line(line, sizeof(line), file); + if (!r) { + CHK(feof(file) == 1); + return 0; + } + CHK(IS_NEW_BLOCK(line, suntime_header) == 1); + n = sscanf(line+strlen(suntime_header), + "%*g %*g %*d-%*d-%*dT%*d:%*d:%*d (%lg %lg %lg)", + dir, dir+1, dir+2); + CHK(n == 3); + + /* Get counts */ + CHK(read_line(line, sizeof(line), file) == 1); + CHK( + sscanf(line, + "%lu %lu %lu %lu %lu", + &counts->global, &counts->receiver, &counts->primary, + &counts->realisation, &counts->failed) == 5); + return 1; +} + +static void +read_global(FILE* file, double* E, double* SE) +{ + char line[MAX_LINE_LEN]; + CHK(read_line(line, sizeof(line), file) == 1); + CHK(sscanf(line, "%lg %lg", E, SE) == 2); +} + +static void +read_recv(FILE* file, char name[], double E[], double SE[]) +{ + char line[MAX_LINE_LEN]; + + CHK(file != NULL); + CHK(name != NULL); + CHK(E != NULL); + CHK(SE != NULL); + + CHK(read_line(line, sizeof(line), file) == 1); + CHK( + sscanf(line, + "%s %*u %*g " + "%lg %lg %lg %lg %lg %lg %lg %lg %lg %lg " + "%lg %lg %lg %lg %lg %lg %lg %lg %lg %lg", + name, /* ID, area */ + &E[FRONT_ABSORBED_FLUX], &SE[FRONT_ABSORBED_FLUX], + &E[FRONT_INCOMING_FLUX], &SE[FRONT_INCOMING_FLUX], + &E[FRONT_ABSORBED_FIELD_GAIN], &SE[FRONT_ABSORBED_FIELD_GAIN], + &E[FRONT_ABSORBED_ATM_GAIN], &SE[FRONT_ABSORBED_ATM_GAIN], + &E[FRONT_EFFICIENCY], &SE[FRONT_EFFICIENCY], + &E[BACK_ABSORBED_FLUX], &SE[BACK_ABSORBED_FLUX], + &E[BACK_INCOMING_FLUX], &SE[BACK_INCOMING_FLUX], + &E[BACK_ABSORBED_FIELD_GAIN], &SE[BACK_ABSORBED_FIELD_GAIN], + &E[BACK_ABSORBED_ATM_GAIN], &SE[BACK_ABSORBED_ATM_GAIN], + &E[BACK_EFFICIENCY], &SE[BACK_EFFICIENCY]) == + 2 * RECEIVER_RESULTS_COUNT__ + 1); +} + +static void +read_primary + (FILE* file, char name[], double* area, double E[], double SE[]) +{ + char line[MAX_LINE_LEN]; + + CHK(file != NULL); + CHK(area != NULL); + CHK(E != NULL); + CHK(SE != NULL); + + CHK(read_line(line, sizeof(line), file) == 1); + CHK( + sscanf(line, + "%s %*u " + "%lg %*u " + "%lg %lg %lg %lg\n", + name, /* ID */ + area, /* count, */ + &E[PRIMARY_COS], &SE[PRIMARY_COS], + &E[PRIMARY_SHADOW], &SE[PRIMARY_SHADOW]) == + 2 * PRIMARY_RESULTS_COUNT__ + 2); +} + + +static void +read_recvXprim + (FILE* file, + unsigned long* rcv_id, + unsigned long* prim_id, + double E[], + double SE[]) +{ + char line[MAX_LINE_LEN]; + + CHK(file != NULL); + CHK(rcv_id != NULL); + CHK(prim_id != NULL); + CHK(E != NULL); + CHK(SE != NULL); + + CHK(read_line(line, sizeof(line), file) == 1); + CHK( + sscanf(line, + "%lu %lu " + "%lg %lg %lg %lg %lg %lg %lg %lg " + "%lg %lg %lg %lg %lg %lg %lg %lg", + rcv_id, prim_id, + &E[FRONT_ABSORBED_FLUX], &SE[FRONT_ABSORBED_FLUX], + &E[FRONT_INCOMING_FLUX], &SE[FRONT_INCOMING_FLUX], + &E[FRONT_ABSORBED_FIELD_GAIN], &SE[FRONT_ABSORBED_FIELD_GAIN], + &E[FRONT_ABSORBED_ATM_GAIN], &SE[FRONT_ABSORBED_ATM_GAIN], + &E[BACK_ABSORBED_FLUX], &SE[BACK_ABSORBED_FLUX], + &E[BACK_INCOMING_FLUX], &SE[BACK_INCOMING_FLUX], + &E[BACK_ABSORBED_FIELD_GAIN], &SE[BACK_ABSORBED_FIELD_GAIN], + &E[BACK_ABSORBED_ATM_GAIN], &SE[BACK_ABSORBED_ATM_GAIN]) == + 2 * (RECEIVER_RESULTS_COUNT__ - 2 /* efficiencies not read */) + 2); +} + +static void +compute_estimate_intersection + (double intersection[2], + const double scale, + const double E0, + const double SE0, + const double E1, + const double SE1) +{ + double interval0[2], interval1[2]; + CHK(scale > 0); + interval0[0] = E0 - scale*SE0; + interval0[1] = E0 + scale*SE0; + interval1[0] = E1 - scale*SE1; + interval1[1] = E1 + scale*SE1; + intersection[0] = MMAX(interval0[0], interval1[0]); + intersection[1] = MMIN(interval0[1], interval1[1]); +} + +static void +check_estimate + (double ref_E, + double ref_SE, + double test_E, + double test_SE) +{ + if(ref_E == -1) { + CHK(ref_SE == -1); + CHK(test_E == -1); + CHK(test_SE == -1); + } else { + double interval[2]; + CHK(ref_SE >= 0); + CHK(test_E >= 0); + CHK(test_SE >= 0); + if(ref_SE == 0) ref_SE = ref_E / 1000.0; + if(test_SE == 0) test_SE = test_E / 1000.0; + compute_estimate_intersection(interval, 2, ref_E, ref_SE, test_E, test_SE); + CHK(interval[0] <= interval[1]); + } +} + +static void +check_1_reference + (FILE* ref_file, + FILE* test_file, + const struct counts* counts) +{ + unsigned n; + + CHK(ref_file != NULL); + CHK(test_file != NULL); + CHK(counts != NULL); + + /* both files' pointer are just past the new bloc header */ + + for(n = 0; n < counts->global; n++) { + double reference_E, reference_SE, test_E, test_SE; + read_global(ref_file, &reference_E, &reference_SE); + read_global(test_file, &test_E, &test_SE); + check_estimate(reference_E, reference_SE, test_E, test_SE); + } + for(n = 0; n < counts->receiver; n++) { + char ref_rcv_name[MAX_LINE_LEN], test_rcv_name[MAX_LINE_LEN]; + double reference_E[RECEIVER_RESULTS_COUNT__]; + double reference_SE[RECEIVER_RESULTS_COUNT__]; + double test_E[RECEIVER_RESULTS_COUNT__]; + double test_SE[RECEIVER_RESULTS_COUNT__]; + enum receiver_result_type r; + + read_recv(ref_file, ref_rcv_name, reference_E, reference_SE); + read_recv(test_file, test_rcv_name, test_E, test_SE); + CHK(strcmp(ref_rcv_name, test_rcv_name) == 0); + FOR_EACH(r, FIRST_RECEIVER_RESULT, RECEIVER_RESULTS_COUNT__) { + check_estimate(reference_E[r], reference_SE[r], test_E[r], test_SE[r]); + } + } + for(n = 0; n < counts->primary; n++) { + char ref_prim_name[MAX_LINE_LEN], test_prim_name[MAX_LINE_LEN]; + double reference_E[PRIMARY_RESULTS_COUNT__]; + double reference_SE[PRIMARY_RESULTS_COUNT__]; + double test_E[PRIMARY_RESULTS_COUNT__]; + double test_SE[PRIMARY_RESULTS_COUNT__]; + double ref_area, test_area; + enum primary_result_type r; + + read_primary(ref_file, ref_prim_name, &ref_area, reference_E, reference_SE); + read_primary(test_file, test_prim_name, &test_area, test_E, test_SE); + check_estimate(ref_area, 0, test_area, 0); + CHK(strcmp(ref_prim_name, test_prim_name) == 0); + FOR_EACH(r, FIRST_PRIMARY_RESULT, PRIMARY_RESULTS_COUNT__) { + check_estimate(reference_E[r], reference_SE[r], test_E[r], test_SE[r]); + } + } + for(n = 0; n < counts->receiver * counts->primary; n++) { + double reference_E[RECEIVER_RESULTS_COUNT__]; + double reference_SE[RECEIVER_RESULTS_COUNT__]; + double test_E[RECEIVER_RESULTS_COUNT__]; + double test_SE[RECEIVER_RESULTS_COUNT__]; + unsigned long ref_rcv_id, ref_prim_id; + unsigned long test_rcv_id, test_prim_id; + + enum receiver_result_type r; + read_recvXprim(ref_file, &ref_rcv_id, &ref_prim_id, reference_E, reference_SE); + read_recvXprim(test_file, &test_rcv_id, &test_prim_id, test_E, test_SE); + /* we rely on the order of outputs */ + CHK(ref_rcv_id == test_rcv_id); + CHK(ref_prim_id == test_prim_id); + FOR_EACH(r, FIRST_RECEIVER_RESULT, RECEIVER_RESULTS_COUNT__) { + if (r == FRONT_EFFICIENCY || r == BACK_EFFICIENCY) + continue; /* not read */ + check_estimate(reference_E[r], reference_SE[r], test_E[r], test_SE[r]); + } + } +} + +static FINLINE int +create_tmp_file(char* name, const size_t max_sizeof_name) +{ + const char* template = "solstice_tmp_file_XXXXXX"; + int fd; + CHK(name != NULL); + CHK(strlen(template)+1 <= max_sizeof_name-1); + strcpy(name, template); + fd = mkstemp(name); + CHK(fd != -1); + return fd; +} + +/* Scem an solstice agree on elevation. + * On the other hand, scem and solstice convention on azimuth differ: + * - scem CW with N=0 + * - solstice CCW with E=0 */ +static FINLINE void +scem_pos_to_solstice_pos + (const struct scem_sun_pos* scem, double solstice[2]) +{ + ASSERT(scem && solstice); + solstice[0] = fmod(360 + 90 - MRAD2DEG(scem->azimuth), 360); + solstice[1] = MRAD2DEG(scem->elevation); +} + +static void +do_check(const char* binary, const char* dir, const char* base_name) +{ + struct counts time_counts, dir_counts; + int n, h; + int err; + double lat, lon; + char cmd[512]; + char time_file_name[128]; + char dir_file_name[128]; + char sdate[64]; + char* p; + struct tm date; + struct scem_sun_pos scem_pos; + struct scem_location loc; + enum scem_sun_algo algorithm; + res_T res; + double sun_dir[3], dir_angles[2], solstice_pos[2]; + FILE* dir_file; + FILE* time_file; + int dir_fd, time_fd; + + lat = -90 + 180 * ((double)rand() / (double)RAND_MAX); + lon = 15 * ((double)rand() / (double)RAND_MAX); /* To allow to sample UTC h */ + h = (int)(9 + 10 * ((double)rand() / (double)RAND_MAX)); + algorithm = SCEM_SUN_PSA; + + snprintf(sdate, sizeof(sdate), "2020-05-05T%d:00:00", h); + p = strptime(sdate, "%Y-%m-%dT%H:%M:%S", &date); + CHK(p && *p == '\0'); + loc.latitude = lat; loc.longitude = lon; + + time_fd = create_tmp_file(time_file_name, sizeof(time_file_name)); + time_file = fdopen(time_fd, "r"); + CHK(time_file != NULL); + + n = snprintf(cmd, sizeof(cmd), + "%s -o %s -f -L %g,%g -T %s -n %lu -R %s%s_receiver.yaml %s%s.yaml", + binary, time_file_name, lat, lon, sdate, 10000L, + dir, base_name, dir, base_name); + CHK((unsigned)n < sizeof(cmd)); + + err = system(cmd); + CHK(err == 0); + + get_dir_and_counts(time_file, sun_dir, &time_counts); + + dir_fd = create_tmp_file(dir_file_name, sizeof(dir_file_name)); + dir_file = fdopen(dir_fd, "r"); + CHK(dir_file != NULL); + + res = scem_sun_position_from_earth(&date, &loc, algorithm, &scem_pos); + CHK(res == RES_OK); + scem_pos_to_solstice_pos(&scem_pos, solstice_pos); + n = snprintf(cmd, sizeof(cmd), + "%s -o %s -f -D %g,%g -n %lu -R %s%s_receiver.yaml %s%s.yaml", + binary, dir_file_name, + SPLIT2(solstice_pos), + 10000L, dir, base_name, dir, base_name); + CHK((unsigned)n < sizeof(cmd)); + + err = system(cmd); + CHK(err == 0); + + get_angles_and_counts(dir_file, dir_angles, &dir_counts); + + CHK(counts_ok(&time_counts, &dir_counts) == 1); + check_1_reference(time_file, dir_file, &time_counts); + + fclose(time_file); + fclose(dir_file); + remove(time_file_name); + remove(dir_file_name); +} + +int +main(int argc, char** argv) +{ + int err = 0; + + if(argc != 4) { + printf("Usage: %s <solstice-binary> <file-path> <file-base-name>\n", argv[0]); + goto error; + } + + do_check(argv[1], argv[2], argv[3]); + +exit: + return err; +error: + err = 1; + goto exit; +} + diff --git a/src/test_solstice_utils.h b/src/test_solstice_utils.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com) * Copyright (C) 2016-2018 CNRS * * This program is free software: you can redistribute it and/or modify diff --git a/yaml/test08.ref b/yaml/test08.ref @@ -1,12 +1,12 @@ #--- Sun direction: 0 63 (-0.45399 -0 -0.891007) 7 1 1 10000 0 -85.5109 0 +85.5106 0 0 0 -0.917002 1.82501e-10 +0.917005 2.78775e-10 0 0 -78.4137 2.86102e-08 +78.4137 9.53674e-09 0 0 0 0 -reflector.pivot.small_square 6 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 78.4137 2.86102e-08 78.4137 2.86102e-08 78.4137 2.86102e-08 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -reflector.pivot.parabol 2 85.5109 10000 0.917002 1.82501e-10 0 0 -6 2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 78.4137 2.86102e-08 78.4137 2.86102e-08 78.4137 2.86102e-08 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +reflector.pivot.small_square 6 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 78.4137 9.53674e-09 78.4137 9.53674e-09 78.4137 9.53674e-09 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +reflector.pivot.parabol 2 85.5106 10000 0.917005 2.78775e-10 0 0 +6 2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 78.4137 9.53674e-09 78.4137 9.53674e-09 78.4137 9.53674e-09 0 0 0 0 0 0 0 0 0 0 0 0 0 0