Skip to content

Commit a8efd58

Browse files
committed
[1.3.41] 2025-07-22
🚨+ NEW PLUG-IN + 🚨 - Added a new `parameteroptimization` plug-in. Credit to Kyle Rizzo for developing this plug-in. - Applied `.clang-format` to all source files. As such, there are a lot of small changes to many files in this commit. - Converted the following plug-ins to use the doctest framework: `energybalance`, `photosynthesis`, `radiation`, `stomatalconductance`, `canopygenerator`, `leafoptics`, `lidar`, `planthydraulics`, `plantarchitecture`, `projectbuilder`, `voxelintersection`, `weberpenntree`, and `boundarylayerconductance`. - Added `Context::listAllPrimitiveDataLabels()` and `Context::listAllObjectDataLabels()` methods to efficiently get a list of all primitive or object data labels that exist. - Added `Context::showPrimitive()` and `Context::showObject()` methods to show a previously hidden primitive or object. - Fixed an error in `Tube::scaleTubeGirth(float S)` where the stored radius value was not being updated (although the actual primitive geometry was). - Split doctest tests into separate files for better organization. - An error was corrected in `Context::cleanDeletedUUIDs(std::vector<std::vector<uint>> &UUIDs)` and `Context::cleanDeletedUUIDs(std::vector<std::vector<std::vector<uint>>> &UUIDs)`, which were checking for object IDs instead of primitive UUIDs. - Modified inclined plate model code to fall back to purely free convection when wind speed is zero, rather than giving NaN. - Re-factored the plug-in to streamline creation of new plant models in the library (see documentation). - Functions for reading/writing of PNG and JPEG files have been removed from the Visualizer. Functions from the Context are now used instead, which reduces redundant code. Co-authored by: Kyle Rizzo <[email protected]>
1 parent 465c243 commit a8efd58

File tree

895 files changed

+193263
-131828
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

895 files changed

+193263
-131828
lines changed

.claude/.gitignore

Whitespace-only changes.

AGENTS.md

Lines changed: 65 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,71 @@
1+
## Common Mistakes to Avoid
2+
- **Never create build directories** - They already exist in all sample projects
3+
- **Never build from `core/` or `plugins/` directories** - Always use `samples/`
4+
- **Never use `mkdir` for build directories** - Just `cd` into the existing build directory
5+
16
## Code Style
2-
- Code style should follow the style given in .clang-format.
3-
- Standard C++ library include headers are listed in the file core/include/global.h. Check these includes to make sure you do not add unnecessary includes.
7+
- Code style should follow the style given in `.clang-format`.
8+
- Standard C++ library include headers are listed in the file `core/include/global.h`. Check these includes to make sure you do not add unnecessary includes.
9+
- Prefer descriptive variable names that 'self-document' the code.
10+
- Prefer clearer code over clever optimized code that provides only marginal performance improvements.
11+
12+
## Code Structure
13+
- The code is organized into a core library and plugins. The core library contains the main functionality, while plugins provide additional features.
14+
- The folder `samples` contains many examples that illustrate how to run the code. Each of these examples contains a `CMakeLists.txt` file that can be used to build the example. This file references `core/CMake_project.cmake`, which is the highest level CMake file for the core library, and also links any plugin CMake files.
15+
- CMakeLists.txt files inside `core` and `plugins` do not build by themselves. You need to build a project that uses `core/CMake_project.cmake` to build the core library and any plugins.
416

517
## Testing
6-
- Do not try to run utilities/run_samples.sh. It will time out.
7-
### Documentation
8-
- If changes are made to docstrings or function signatures, build the documentation file `doc/Doxyfile` using doxygen. Some notes on this are given below:
18+
19+
### Test File Organization
20+
- Core tests are located in `core/tests/` with 5 main test header files:
21+
- `Test_utilities.h`: Vector types, colors, dates/times, coordinate systems
22+
- `Test_functions.h`: Global helper functions and math utilities
23+
- `Test_XML.h`: XML parsing functions
24+
- `Test_context.h`: Context class methods
25+
- `Test_data.h`: Context data management (primitive, object, global data)
26+
- Plugin tests are in `plugins/[plugin_name]/tests/selfTest.cpp`
27+
- Tests use the doctest framework with specific patterns (DOCTEST_TEST_CASE, DOCTEST_CHECK, etc.)
28+
- When adding new functions or classes, always add a test for it in the appropriate test file.
29+
30+
### Build and Test Workflow
31+
1. **Building Tests**: Always build from a sample directory, not from core or plugins directly
32+
```bash
33+
cd samples/[project_name]/
34+
cd build # The build directory already exists - DO NOT create it with mkdir
35+
cmake .. && make
36+
```
37+
**IMPORTANT**: Never use `mkdir` to create build directories - they already exist in all sample projects
38+
39+
2. **Running Tests**:
40+
- For core tests: `./context_tests` (from context_selftest/build/)
41+
- For plugin tests: `./{plugin_name}_selftest` or `./{plugin_name}_tests`
42+
43+
3. **Common Build Issues**:
44+
- Always check compilation errors carefully for missing includes or function signature mismatches
45+
- Plugin tests may have different data label expectations than actual implementation (verify against source code)
46+
- Tests failing with "does not exist" errors usually indicate incorrect data labels in tests
47+
48+
### Test Coverage
49+
- The script `utilities/generate_coverage_report.sh` can be used to check test coverage.
50+
- The script must be run from the build directory. For example, `cd samples/context_selftest/build && ../../../utilities/generate_coverage_report.sh -t context_tests -r html -l logfile.log`.
51+
- **Prefer HTML output (`-r html`)** over text output - it provides a clear summary table with color-coded coverage percentages and clickable file navigation in `coverage/index.html`.
52+
- Coverage analysis requires tests to compile and run successfully first
53+
- Coverage improvements should focus on:
54+
- Adding tests for uncovered functions (check function names against test coverage)
55+
- Verifying data manipulation functions use correct parameter types and labels
56+
- Testing edge cases and error conditions
57+
- Ensuring tests are organized with similar functionality and non-redundant
58+
59+
### Debugging Plugin Tests
60+
- When plugin tests fail, first check if the test expectations match the actual implementation
61+
- Always verify test expectations against the source code in `plugins/[name]/src/`
62+
- Tests should not write any errors messages to std::cerr. Use the struct `capture_cerr` (defined in `core/include/global.h`) to capture any error messages and check them in the test.
63+
- Make sure that any functions marked `[[nodiscard]]` assign their return value to a variable in the test, otherwise the compiler will issue a warning.
64+
65+
## Documentation
66+
- When adding new functions or classes, always add a docstring to the header file, and consider whether additional documentation is needed (in `doc/*.dox` for the core or `plugins/[plugin_name]/doc/*.dox` for plugins).
67+
- If changes are made to docstrings or function signatures, build the documentation file `doc/Doxyfile` using doxygen to test it. Some notes on this are given below:
968
- Run `doxygen doc/Doxyfile` to generate the documentation from the root directory not the `doc` directory.
1069
- Check doxygen output for warnings. It is ok to ignore warnings of the form " warning: Member XXX is not documented."
1170
- The main concern when changing docstrings or function signatures is the potential for breaking \ref references, which produces a warning like: warning: unable to resolve reference to XXX for \ref command".
12-
When fixing these, don't just remove the funciton signature or use markdown `` ticks to suppress the warning. It is important to keep the signature in the reference for overloaded functions/methods. Figure out why the function signature is not matching and thus causing Doxygen to treat it as plain text.
13-
- You tend to flag hash symbols in code blocks as erroneous and propose to add a double hash (e.g., '##include "Context.h"'). This is not needed and ends up rendering the double hash.
14-
### Test Coverage
15-
- The script `utilitiesgenerate_coverage_report.sh` can be used to check test coverage.
16-
- It is recommended to use text-based output, which is achieved with the `-r text` option.
71+
When fixing these, don't just remove the funciton signature or use markdown `` ticks to suppress the warning. It is important to keep the signature in the reference for overloaded functions/methods. Figure out why the function signature is not matching and thus causing Doxygen to treat it as plain text.

benchmarks/energy_balance_dragon/main.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
using namespace helios;
44

5-
int main(){
6-
uint Nx = 500; // grid size in x
7-
uint Ny = 500; // grid size in y
8-
float D = 10.f; // domain width
5+
int main() {
6+
uint Nx = 500; // grid size in x
7+
uint Ny = 500; // grid size in y
8+
float D = 10.f; // domain width
99
vec2 ground_size(D, D);
1010

1111
std::ofstream outfile("../results/runtime.txt");
@@ -21,7 +21,7 @@ int main(){
2121
outfile << "PLY model load, " << elapsed << "\n";
2222

2323
timer.tic();
24-
ground_UUIDs = context.addTile(nullorigin, ground_size, nullrotation, make_int2(Nx, Ny) );
24+
ground_UUIDs = context.addTile(nullorigin, ground_size, nullrotation, make_int2(Nx, Ny));
2525
elapsed = timer.toc("Ground geometry creation");
2626
outfile << "Ground geometry creation, " << elapsed << "\n";
2727

@@ -51,4 +51,4 @@ int main(){
5151

5252
outfile.close();
5353
return EXIT_SUCCESS;
54-
}
54+
}

benchmarks/plant_architecture_bean/main.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
using namespace helios;
44

5-
int main(){
6-
uint Nx = 5; // number of plants in x-direction
7-
uint Ny = 5; // number of plants in y-direction
8-
vec2 spacing(0.3f, 0.3f); // plant spacing
9-
float age = 45.f; // age of plants in days
5+
int main() {
6+
uint Nx = 5; // number of plants in x-direction
7+
uint Ny = 5; // number of plants in y-direction
8+
vec2 spacing(0.3f, 0.3f); // plant spacing
9+
float age = 45.f; // age of plants in days
1010

1111
std::ofstream outfile("../results/runtime.txt");
1212
Timer timer;
@@ -23,4 +23,4 @@ int main(){
2323

2424
outfile.close();
2525
return EXIT_SUCCESS;
26-
}
26+
}

benchmarks/radiation_homogeneous_canopy/main.cpp

Lines changed: 27 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@
22

33
using namespace helios;
44

5-
int main(){
5+
int main() {
66

77
bool failure = false;
88

99
uint Ndirect = 1024;
1010
uint Ndiffuse = 1024;
1111

12-
float D = 20; //domain width
13-
float LAI = 1.0; //canopy leaf area index
14-
float h = 3; //canopy height
15-
float w_leaf = 0.05; //leaf width
12+
float D = 20; // domain width
13+
float LAI = 1.0; // canopy leaf area index
14+
float h = 3; // canopy height
15+
float w_leaf = 0.05; // leaf width
1616

1717
std::ofstream outfile("../results/runtime.txt");
1818

@@ -23,42 +23,41 @@ int main(){
2323

2424
timer.tic();
2525

26-
uint objID_ptype = context.addTileObject( make_vec3(0,0,0), make_vec2(w_leaf,w_leaf), make_SphericalCoord(0,0), make_int2(2,2), "plugins/radiation/disk.png" );
26+
uint objID_ptype = context.addTileObject(make_vec3(0, 0, 0), make_vec2(w_leaf, w_leaf), make_SphericalCoord(0, 0), make_int2(2, 2), "plugins/radiation/disk.png");
2727
std::vector<uint> UUIDs_ptype = context.getObjectPointer(objID_ptype)->getPrimitiveUUIDs();
2828

2929
float A_leaf = 0;
30-
for( uint p=0; p<UUIDs_ptype.size(); p++ ){
30+
for (uint p = 0; p < UUIDs_ptype.size(); p++) {
3131
A_leaf += context.getPrimitiveArea(UUIDs_ptype.at(p));
3232
}
3333

34-
int Nleaves = round(LAI*D*D/A_leaf);
34+
int Nleaves = round(LAI * D * D / A_leaf);
3535

3636
std::vector<uint> UUIDs_leaf;
3737

38-
for( int i=0; i<Nleaves; i++ ){
38+
for (int i = 0; i < Nleaves; i++) {
3939

40-
vec3 position( (-0.5+context.randu())*D, (-0.5+context.randu())*D, 0.5*w_leaf+context.randu()*h );
40+
vec3 position((-0.5 + context.randu()) * D, (-0.5 + context.randu()) * D, 0.5 * w_leaf + context.randu() * h);
4141

42-
SphericalCoord rotation( 1.f, acos(1.f-context.randu()), 2.f*M_PI*context.randu() );
42+
SphericalCoord rotation(1.f, acos(1.f - context.randu()), 2.f * M_PI * context.randu());
4343

44-
uint objID = context.copyObject( objID_ptype );
44+
uint objID = context.copyObject(objID_ptype);
4545

46-
context.getObjectPointer( objID )->rotate( -rotation.elevation, "y" );
47-
context.getObjectPointer( objID )->rotate( rotation.azimuth, "z" );
46+
context.getObjectPointer(objID)->rotate(-rotation.elevation, "y");
47+
context.getObjectPointer(objID)->rotate(rotation.azimuth, "z");
4848

49-
context.getObjectPointer( objID )->translate( position );
49+
context.getObjectPointer(objID)->translate(position);
5050

51-
std::vector<uint> UUIDs = context.getObjectPointer( objID )->getPrimitiveUUIDs();
52-
53-
UUIDs_leaf.insert( UUIDs_leaf.end(), UUIDs.begin(), UUIDs.end() );
51+
std::vector<uint> UUIDs = context.getObjectPointer(objID)->getPrimitiveUUIDs();
5452

53+
UUIDs_leaf.insert(UUIDs_leaf.end(), UUIDs.begin(), UUIDs.end());
5554
}
5655

57-
context.deleteObject( objID_ptype );
56+
context.deleteObject(objID_ptype);
5857

59-
std::vector<uint> UUIDs_ground = context.addTile( make_vec3(0,0,0), make_vec2(D,D), make_SphericalCoord(0,0), make_int2(100,100) );
58+
std::vector<uint> UUIDs_ground = context.addTile(make_vec3(0, 0, 0), make_vec2(D, D), make_SphericalCoord(0, 0), make_int2(100, 100));
6059

61-
context.setPrimitiveData( UUIDs_ground, "twosided_flag", uint(0) );
60+
context.setPrimitiveData(UUIDs_ground, "twosided_flag", uint(0));
6261

6362
elapsed = timer.toc("Geometry creation");
6463
outfile << "Geometry Creation, " << elapsed << "\n";
@@ -68,15 +67,15 @@ int main(){
6867

6968
radiation.addRadiationBand("direct");
7069
radiation.disableEmission("direct");
71-
radiation.setDirectRayCount("direct",Ndirect);
72-
float theta_s = 0.2*M_PI;
73-
uint ID = radiation.addCollimatedRadiationSource( make_SphericalCoord(0.5*M_PI-theta_s,0.f) );
74-
radiation.setSourceFlux(ID,"direct",1.f/cos(theta_s));
70+
radiation.setDirectRayCount("direct", Ndirect);
71+
float theta_s = 0.2 * M_PI;
72+
uint ID = radiation.addCollimatedRadiationSource(make_SphericalCoord(0.5 * M_PI - theta_s, 0.f));
73+
radiation.setSourceFlux(ID, "direct", 1.f / cos(theta_s));
7574

7675
radiation.addRadiationBand("diffuse");
7776
radiation.disableEmission("diffuse");
78-
radiation.setDiffuseRayCount("diffuse",Ndiffuse);
79-
radiation.setDiffuseRadiationFlux("diffuse",1.f);
77+
radiation.setDiffuseRayCount("diffuse", Ndiffuse);
78+
radiation.setDiffuseRadiationFlux("diffuse", 1.f);
8079

8180
radiation.enforcePeriodicBoundary("xy");
8281

@@ -104,5 +103,4 @@ int main(){
104103
outfile.close();
105104

106105
return EXIT_SUCCESS;
107-
108-
}
106+
}

core/CMakeLists.txt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,15 @@ endif()
99
# Defining Helios Context source files to be built
1010
set(HELIOS_SOURCES
1111
src/Context.cpp
12+
src/Context_primitive.cpp
13+
src/Context_object.cpp
1214
src/Context_fileIO.cpp
1315
src/Context_data.cpp
1416
src/global.cpp
15-
src/selfTest.cpp
17+
tests/selfTest.cpp
1618
lib/pugixml/pugixml.cpp
1719
)
20+
1821
add_library(helios STATIC ${HELIOS_SOURCES})
1922

2023
# Defining Helios Context include files
@@ -50,6 +53,6 @@ file( COPY "${CMAKE_CURRENT_SOURCE_DIR}/lib/images" DESTINATION "${CMAKE_BINARY_
5053
file( COPY "${CMAKE_CURRENT_SOURCE_DIR}/lib/models" DESTINATION "${CMAKE_BINARY_DIR}/lib/" )
5154
file( COPY "${CMAKE_CURRENT_SOURCE_DIR}/lib/testdata" DESTINATION "${CMAKE_BINARY_DIR}/lib/" )
5255

53-
add_executable( context_tests "src/TestMain.cpp" )
56+
add_executable( context_tests tests/TestMain.cpp )
5457
target_link_libraries( context_tests PRIVATE helios )
55-
add_test( NAME context_tests COMMAND context_tests )
58+
add_test( NAME context_tests COMMAND context_tests )

core/CMake_project.cmake

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,43 @@ if(GIT_FOUND)
1919
OUTPUT_STRIP_TRAILING_WHITESPACE
2020
)
2121
message( STATUS "[Helios] Detected Git commit hash: ${GIT_COMMIT_HASH}" )
22+
23+
# Get current branch name
24+
execute_process(
25+
COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD
26+
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
27+
OUTPUT_VARIABLE CURRENT_BRANCH
28+
OUTPUT_STRIP_TRAILING_WHITESPACE
29+
ERROR_QUIET
30+
RESULT_VARIABLE BRANCH_RESULT
31+
)
32+
33+
# Only check for updates if we're on master/main branch
34+
if(BRANCH_RESULT EQUAL 0 AND (CURRENT_BRANCH STREQUAL "master" OR CURRENT_BRANCH STREQUAL "main"))
35+
execute_process(
36+
COMMAND ${GIT_EXECUTABLE} fetch --dry-run
37+
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
38+
OUTPUT_QUIET
39+
ERROR_QUIET
40+
RESULT_VARIABLE FETCH_RESULT
41+
)
42+
43+
if(FETCH_RESULT EQUAL 0)
44+
# Check if local branch is behind remote
45+
execute_process(
46+
COMMAND ${GIT_EXECUTABLE} rev-list HEAD..origin/${CURRENT_BRANCH} --count
47+
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
48+
OUTPUT_VARIABLE COMMITS_BEHIND
49+
OUTPUT_STRIP_TRAILING_WHITESPACE
50+
ERROR_QUIET
51+
RESULT_VARIABLE REV_LIST_RESULT
52+
)
53+
54+
if(REV_LIST_RESULT EQUAL 0 AND COMMITS_BEHIND AND NOT COMMITS_BEHIND STREQUAL "0")
55+
message(WARNING "[Helios] Your local ${CURRENT_BRANCH} branch is ${COMMITS_BEHIND} commit(s) behind the remote ${CURRENT_BRANCH} branch. Consider updating with 'git pull origin ${CURRENT_BRANCH}'.")
56+
endif()
57+
endif()
58+
endif()
2259
else()
2360
set(GIT_COMMIT_HASH "unknown")
2461
endif()

0 commit comments

Comments
 (0)