Skip to content

Conversation

@nravanelli
Copy link

@nravanelli nravanelli commented Nov 4, 2025

Added 2 biophysical fan use models from Foster et al. 2021 (occupational) and Morris et al. 2021 (rest). Each use a similar concept for determining the net heat storage benefit with fan use relative to still air. The occupational model uses different clothing assumptions and net air velocity (incorporating self generated airflow). I therefore kept them separate and reference both papers independently. The occupational model adopts the interpretation provided of the net difference in heat storage. The models were tested and reproduce the same reference figures/conclusions provided by both peer reviewed papers.

[ X ] New tests added and passing.
[ X ] Docstring updated with examples and applicability limits.
[ X ] Documentation (autofunction) updated if public API changed.
[ X ] CHANGELOG updated (if applicable).
[ X ] Linting and formatting applied.
[ X ] All CI checks pass.

New files added to the repository:
new file: pythermalcomfort/models/bfu_rest.py
new file: pythermalcomfort/models/bfu_occupational.py
new file: tests/test_bfu_occupational.py
new file: tests/test_bfu_rest.py

@FedericoTartarini I think the linting/formatting I ran created small changes in linespacing in my version. Nothing else.

Summary by CodeRabbit

Release Notes – Version 3.9.0

  • New Features

    • Introduced two biophysical fan use models—Resting and Occupational—for assessing safe fan operation in various thermal environments. Models compute heat storage, evaporative capacity, and comfort outcomes with and without fans across different activity levels and metabolic rates.
  • Documentation

    • Updated documentation with new model information and added supporting scientific references (Morris, 2021; Foster, 2021).
  • Tests

    • Added test coverage for the new models.
  • Chores

    • Updated contributors.

FedericoTartarini and others added 6 commits September 22, 2025 18:06
…BuiltEnvironment/development

Development
…BuiltEnvironment/development

Development
…BuiltEnvironment/development

docs(changelog): Improve naming and formatting in changelog
…BuiltEnvironment/development

development
…BuiltEnvironment/development

development
	new file:   pythermalcomfort/models/bfu_rest.py
	new file:   tests/test_bfu_occupational.py
	new file:   tests/test_bfu_rest.py
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 4, 2025

Walkthrough

This PR introduces two biophysical fan use (BFU) models for evaluating safe fan operation: a resting model based on Morris2021 and an occupational model based on Foster2021. New input and output dataclasses support these models alongside comprehensive documentation, changelog updates, and test coverage.

Changes

Cohort / File(s) Summary
Documentation & Metadata
AUTHORS.rst, CHANGELOG.rst, docs/documentation/models.rst, docs/documentation/references.rst
Added contributor entry, documented new version 3.9.0 with BFU models, created model documentation sections with autofunction/autoclass references, and registered two scholarly references (Morris2021, Foster2021) supporting the models.
Input Dataclasses
pythermalcomfort/classes_input.py
Introduced BFU_rest_Groups enum, BFU_rest_Parameters (frozen dataclass), BFU_rest_Inputs, BFU_occupational_Inputs, and ScaleWindSpeedLogInputs with comprehensive validation logic for group membership, relative humidity ranges, activity/position constraints, and broadcasting compatibility.
Output Dataclasses
pythermalcomfort/classes_return.py
Added BFU_rest and BFU_occupational frozen dataclasses extending AutoStrMixin to encapsulate model results with heat storage, evaporative requirements, and interpretation fields.
Model Implementations
pythermalcomfort/models/bfu_occupational.py, pythermalcomfort/models/bfu_rest.py
Implemented occupational BFU model computing heat storage, dry/evaporative heat, and sweat efficiency across activity types, and resting model computing storage deltas and evaporative limits via Morris LPH methodology; both support scalar/array broadcasting.
API Exports
pythermalcomfort/models/__init__.py
Exposed BFU_occupational and BFU_rest functions in public API via imports and __all__ list.
Test Coverage
tests/test_bfu_occupational.py, tests/test_bfu_rest.py
Added scalar/array input validation tests, interpretation mapping verification, error handling for invalid positions/groups, and vectorised output shape assertions.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Model as BFU Model<br/>(Rest or Occupational)
    participant Inputs as Input Dataclass<br/>(Validation)
    participant Calc as Calculation Engine<br/>(Physics)
    participant Outputs as Output Dataclass

    User->>Model: Call with tdb, rh, params
    Model->>Inputs: Create inputs (validate)
    alt Validation Fails
        Inputs-->>User: ValueError
    end
    Inputs-->>Model: Valid inputs
    Model->>Calc: Broadcast & compute heat transfer
    Calc->>Calc: Calculate respiratory, evaporative,<br/>storage with/without fan
    Calc-->>Model: Computed results
    Model->>Outputs: Create result dataclass
    Outputs-->>User: Return BFU_rest/BFU_occupational
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas requiring attention:

  • Duplication in classes_input.py: The summary notes that BFU_rest_Parameters and BFU_rest_Inputs appear twice in the file. Verify this is intentional or represents unintended duplication that should be removed.
  • Broadcasting logic in bfu_occupational.py: Complex array handling across multiple input parameters (position, activity, met, clo, velocities) with hierarchical error messages. Confirm broadcasting compatibility checks are exhaustive.
  • Physics calculations in bfu_rest.py and bfu_occupational.py: Dense numerical logic involving saturation vapour pressure, Lewis relation, heat transfer coefficients, and sweat efficiency. Cross-reference against Morris2021 and Foster2021 methodologies.
  • Input validation coverage: Ensure all constraints (RH [0, 100], position/activity enums, non-negativity) are consistently enforced across both model entry points and dataclass validators.
  • Backward compatibility wrappers: Verify BFU_occupational() and BFU_rest() function signatures correctly delegate to underlying implementations without signature drift.

Possibly related PRs

  • PR #236: Modifies ScaleWindSpeedLogInputs and wind-scaling feature types, directly intersecting with the new input dataclass added in this PR.
  • PR #189: Updates AutoStrMixin and frozen dataclass repr/__getitem__ behaviour, affecting the new BFU_rest and BFU_occupational result dataclasses that inherit from AutoStrMixin.
  • PR #187: Earlier changes to classes_return.py for dataclass result types and AutoStrMixin inheritance patterns, providing foundation for the new BFU result dataclasses.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarises the main change: addition of two biophysical fan use models (rest and occupational). It's specific, concise, and directly reflects the primary contribution of this changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e73fc2c and ed4d584.

📒 Files selected for processing (52)
  • AUTHORS.rst (1 hunks)
  • CHANGELOG.rst (1 hunks)
  • docs/documentation/models.rst (1 hunks)
  • docs/documentation/references.rst (1 hunks)
  • pythermalcomfort/classes_input.py (2 hunks)
  • pythermalcomfort/classes_return.py (1 hunks)
  • pythermalcomfort/cli.py (0 hunks)
  • pythermalcomfort/jos3_functions/construction.py (1 hunks)
  • pythermalcomfort/jos3_functions/matrix.py (1 hunks)
  • pythermalcomfort/jos3_functions/parameters.py (1 hunks)
  • pythermalcomfort/jos3_functions/thermoregulation.py (3 hunks)
  • pythermalcomfort/models/__init__.py (2 hunks)
  • pythermalcomfort/models/adaptive_ashrae.py (0 hunks)
  • pythermalcomfort/models/adaptive_en.py (0 hunks)
  • pythermalcomfort/models/ankle_draft.py (1 hunks)
  • pythermalcomfort/models/at.py (1 hunks)
  • pythermalcomfort/models/bfu_occupational.py (1 hunks)
  • pythermalcomfort/models/bfu_rest.py (1 hunks)
  • pythermalcomfort/models/clo_tout.py (1 hunks)
  • pythermalcomfort/models/cooling_effect.py (1 hunks)
  • pythermalcomfort/models/discomfort_index.py (0 hunks)
  • pythermalcomfort/models/esi.py (0 hunks)
  • pythermalcomfort/models/heat_index_lu.py (1 hunks)
  • pythermalcomfort/models/heat_index_rothfusz.py (1 hunks)
  • pythermalcomfort/models/humidex.py (1 hunks)
  • pythermalcomfort/models/jos3.py (3 hunks)
  • pythermalcomfort/models/pet_steady.py (1 hunks)
  • pythermalcomfort/models/phs.py (1 hunks)
  • pythermalcomfort/models/pmv_a.py (1 hunks)
  • pythermalcomfort/models/pmv_athb.py (1 hunks)
  • pythermalcomfort/models/pmv_e.py (1 hunks)
  • pythermalcomfort/models/pmv_ppd_iso.py (0 hunks)
  • pythermalcomfort/models/ridge_regression_predict_t_re_t_sk.py (2 hunks)
  • pythermalcomfort/models/set_tmp.py (0 hunks)
  • pythermalcomfort/models/solar_gain.py (1 hunks)
  • pythermalcomfort/models/thi.py (1 hunks)
  • pythermalcomfort/models/two_nodes_gagge.py (1 hunks)
  • pythermalcomfort/models/two_nodes_gagge_ji.py (1 hunks)
  • pythermalcomfort/models/two_nodes_gagge_sleep.py (1 hunks)
  • pythermalcomfort/models/use_fans_heatwaves.py (1 hunks)
  • pythermalcomfort/models/utci.py (0 hunks)
  • pythermalcomfort/models/vertical_tmp_grad_ppd.py (1 hunks)
  • pythermalcomfort/models/wbgt.py (1 hunks)
  • pythermalcomfort/models/wind_chill_temperature.py (0 hunks)
  • pythermalcomfort/models/work_capacity_dunne.py (0 hunks)
  • pythermalcomfort/models/work_capacity_hothaps.py (0 hunks)
  • pythermalcomfort/models/work_capacity_iso.py (1 hunks)
  • pythermalcomfort/models/work_capacity_niosh.py (1 hunks)
  • pythermalcomfort/utilities.py (3 hunks)
  • pythermalcomfort/utils/scale_wind_speed_log.py (1 hunks)
  • tests/test_bfu_occupational.py (1 hunks)
  • tests/test_bfu_rest.py (1 hunks)
💤 Files with no reviewable changes (11)
  • pythermalcomfort/models/utci.py
  • pythermalcomfort/models/adaptive_en.py
  • pythermalcomfort/models/work_capacity_hothaps.py
  • pythermalcomfort/models/discomfort_index.py
  • pythermalcomfort/models/pmv_ppd_iso.py
  • pythermalcomfort/models/adaptive_ashrae.py
  • pythermalcomfort/models/esi.py
  • pythermalcomfort/models/wind_chill_temperature.py
  • pythermalcomfort/models/work_capacity_dunne.py
  • pythermalcomfort/models/set_tmp.py
  • pythermalcomfort/cli.py
🧰 Additional context used
📓 Path-based instructions (9)
docs/**/*.rst

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

docs/**/*.rst: Add an '.. autofunction:: pythermalcomfort.models..' entry to the docs for public APIs
Update the appropriate docs reference file that aggregates API docs when adding a public function

Files:

  • docs/documentation/models.rst
  • docs/documentation/references.rst
pythermalcomfort/**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

pythermalcomfort/**/*.py: Use type hints and NumPy-style docstrings for all library code
Validate inputs in dataclass post_init (call super().post_init() if inheriting BaseInputs)
Convert pandas Series to list/np.array and normalize numeric inputs with np.asarray in post_init
Use validate_type(...) for type checks; raise TypeError for wrong types and ValueError for invalid values
Enforce physical constraints in validation (e.g., non-negativity, z2>z0, z1>z0, z2>z1) with clear error messages
Use numpy operations for numerical work (e.g., np.log, np.asarray) and avoid math.log when arrays are accepted
Validate domains before logs/roots (arguments must be > 0)
Support vectorized behavior; use np.atleast_1d, np.broadcast_to, and check broadcast compatibility
Follow repository patterns: dataclasses, BaseInputs metadata, validate_type, Units/Enums (Postures, WorkIntensity, Sex)
Functions should be small, single-purpose, and include concise, runnable examples in NumPy-style docstrings
Run ruff (lint/format) and docformatter; ensure code is formatted before PR
When accepting arrays, ensure computations are vectorized and avoid per-element Python loops

Files:

  • pythermalcomfort/utils/scale_wind_speed_log.py
  • pythermalcomfort/models/jos3.py
  • pythermalcomfort/models/at.py
  • pythermalcomfort/jos3_functions/parameters.py
  • pythermalcomfort/jos3_functions/thermoregulation.py
  • pythermalcomfort/utilities.py
  • pythermalcomfort/models/work_capacity_iso.py
  • pythermalcomfort/models/clo_tout.py
  • pythermalcomfort/jos3_functions/matrix.py
  • pythermalcomfort/models/phs.py
  • pythermalcomfort/models/heat_index_lu.py
  • pythermalcomfort/models/pet_steady.py
  • pythermalcomfort/models/bfu_rest.py
  • pythermalcomfort/models/two_nodes_gagge.py
  • pythermalcomfort/models/solar_gain.py
  • pythermalcomfort/models/ridge_regression_predict_t_re_t_sk.py
  • pythermalcomfort/models/use_fans_heatwaves.py
  • pythermalcomfort/models/humidex.py
  • pythermalcomfort/classes_input.py
  • pythermalcomfort/models/thi.py
  • pythermalcomfort/models/work_capacity_niosh.py
  • pythermalcomfort/models/pmv_e.py
  • pythermalcomfort/models/vertical_tmp_grad_ppd.py
  • pythermalcomfort/models/pmv_athb.py
  • pythermalcomfort/models/heat_index_rothfusz.py
  • pythermalcomfort/models/ankle_draft.py
  • pythermalcomfort/models/pmv_a.py
  • pythermalcomfort/models/cooling_effect.py
  • pythermalcomfort/models/__init__.py
  • pythermalcomfort/classes_return.py
  • pythermalcomfort/models/bfu_occupational.py
  • pythermalcomfort/jos3_functions/construction.py
  • pythermalcomfort/models/two_nodes_gagge_ji.py
  • pythermalcomfort/models/two_nodes_gagge_sleep.py
  • pythermalcomfort/models/wbgt.py
pythermalcomfort/models/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

pythermalcomfort/models/*.py: Place new domain model functions in pythermalcomfort/models/.py
Include applicability limits and minimal runnable examples in model function docstrings

Files:

  • pythermalcomfort/models/jos3.py
  • pythermalcomfort/models/at.py
  • pythermalcomfort/models/work_capacity_iso.py
  • pythermalcomfort/models/clo_tout.py
  • pythermalcomfort/models/phs.py
  • pythermalcomfort/models/heat_index_lu.py
  • pythermalcomfort/models/pet_steady.py
  • pythermalcomfort/models/bfu_rest.py
  • pythermalcomfort/models/two_nodes_gagge.py
  • pythermalcomfort/models/solar_gain.py
  • pythermalcomfort/models/ridge_regression_predict_t_re_t_sk.py
  • pythermalcomfort/models/use_fans_heatwaves.py
  • pythermalcomfort/models/humidex.py
  • pythermalcomfort/models/thi.py
  • pythermalcomfort/models/work_capacity_niosh.py
  • pythermalcomfort/models/pmv_e.py
  • pythermalcomfort/models/vertical_tmp_grad_ppd.py
  • pythermalcomfort/models/pmv_athb.py
  • pythermalcomfort/models/heat_index_rothfusz.py
  • pythermalcomfort/models/ankle_draft.py
  • pythermalcomfort/models/pmv_a.py
  • pythermalcomfort/models/cooling_effect.py
  • pythermalcomfort/models/__init__.py
  • pythermalcomfort/models/bfu_occupational.py
  • pythermalcomfort/models/two_nodes_gagge_ji.py
  • pythermalcomfort/models/two_nodes_gagge_sleep.py
  • pythermalcomfort/models/wbgt.py
AUTHORS.rst

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Optionally add the contributor to AUTHORS.rst

Files:

  • AUTHORS.rst
pythermalcomfort/utilities.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place generic helper functions in pythermalcomfort/utilities.py

Files:

  • pythermalcomfort/utilities.py
pythermalcomfort/{models,utilities}.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Ensure docstring sections include Args (with units), Returns, Raises, Examples, Applicability

Files:

  • pythermalcomfort/utilities.py
CHANGELOG.*

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Update the CHANGELOG for public API changes

Files:

  • CHANGELOG.rst
pythermalcomfort/classes_input.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Add or update input dataclasses in pythermalcomfort/classes_input.py (or adjacent to the function when appropriate)

Files:

  • pythermalcomfort/classes_input.py
tests/**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

tests/**/*.py: Write tests covering scalars, arrays, broadcasting, and invalid inputs (TypeError/ValueError)
Use numpy.testing or pytest.approx for numeric assertions
Ensure tests are deterministic, small, and pass locally with pytest before PR

Files:

  • tests/test_bfu_occupational.py
  • tests/test_bfu_rest.py
🧠 Learnings (15)
📓 Common learnings
Learnt from: CR
Repo: CenterForTheBuiltEnvironment/pythermalcomfort PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T01:23:02.859Z
Learning: Applies to pythermalcomfort/**/*.py : Follow repository patterns: dataclasses, BaseInputs metadata, validate_type, Units/Enums (Postures, WorkIntensity, Sex)
Learnt from: CR
Repo: CenterForTheBuiltEnvironment/pythermalcomfort PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T01:23:02.859Z
Learning: Applies to pythermalcomfort/models/*.py : Include applicability limits and minimal runnable examples in model function docstrings
Learnt from: CR
Repo: CenterForTheBuiltEnvironment/pythermalcomfort PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T01:23:02.859Z
Learning: Applies to pythermalcomfort/{models,utilities}.py : Ensure docstring sections include Args (with units), Returns, Raises, Examples, Applicability
📚 Learning: 2025-09-15T01:23:02.859Z
Learnt from: CR
Repo: CenterForTheBuiltEnvironment/pythermalcomfort PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T01:23:02.859Z
Learning: Applies to docs/**/*.rst : Add an '.. autofunction:: pythermalcomfort.models.<module>.<func>' entry to the docs for public APIs

Applied to files:

  • docs/documentation/models.rst
  • pythermalcomfort/models/at.py
  • pythermalcomfort/jos3_functions/parameters.py
  • pythermalcomfort/jos3_functions/thermoregulation.py
  • pythermalcomfort/utilities.py
  • pythermalcomfort/models/clo_tout.py
  • pythermalcomfort/models/phs.py
  • pythermalcomfort/models/solar_gain.py
  • pythermalcomfort/models/heat_index_rothfusz.py
  • pythermalcomfort/models/cooling_effect.py
  • pythermalcomfort/models/__init__.py
  • pythermalcomfort/jos3_functions/construction.py
📚 Learning: 2025-09-15T01:23:02.859Z
Learnt from: CR
Repo: CenterForTheBuiltEnvironment/pythermalcomfort PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T01:23:02.859Z
Learning: Applies to pythermalcomfort/**/*.py : Follow repository patterns: dataclasses, BaseInputs metadata, validate_type, Units/Enums (Postures, WorkIntensity, Sex)

Applied to files:

  • docs/documentation/models.rst
  • pythermalcomfort/jos3_functions/thermoregulation.py
  • pythermalcomfort/utilities.py
  • pythermalcomfort/models/work_capacity_iso.py
  • pythermalcomfort/classes_input.py
  • pythermalcomfort/classes_return.py
  • pythermalcomfort/models/bfu_occupational.py
  • tests/test_bfu_occupational.py
📚 Learning: 2025-09-15T01:23:02.859Z
Learnt from: CR
Repo: CenterForTheBuiltEnvironment/pythermalcomfort PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T01:23:02.859Z
Learning: Applies to pythermalcomfort/utilities.py : Place generic helper functions in pythermalcomfort/utilities.py

Applied to files:

  • docs/documentation/models.rst
  • pythermalcomfort/jos3_functions/parameters.py
  • pythermalcomfort/jos3_functions/thermoregulation.py
  • pythermalcomfort/utilities.py
  • pythermalcomfort/models/clo_tout.py
  • pythermalcomfort/jos3_functions/matrix.py
  • pythermalcomfort/jos3_functions/construction.py
📚 Learning: 2025-09-15T01:23:02.859Z
Learnt from: CR
Repo: CenterForTheBuiltEnvironment/pythermalcomfort PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T01:23:02.859Z
Learning: Applies to pythermalcomfort/{models,utilities}.py : Ensure docstring sections include Args (with units), Returns, Raises, Examples, Applicability

Applied to files:

  • pythermalcomfort/utils/scale_wind_speed_log.py
  • pythermalcomfort/models/jos3.py
  • pythermalcomfort/models/at.py
  • pythermalcomfort/jos3_functions/parameters.py
  • pythermalcomfort/jos3_functions/thermoregulation.py
  • pythermalcomfort/utilities.py
  • pythermalcomfort/models/work_capacity_iso.py
  • pythermalcomfort/models/clo_tout.py
  • pythermalcomfort/models/phs.py
  • pythermalcomfort/models/heat_index_lu.py
  • pythermalcomfort/models/pet_steady.py
  • pythermalcomfort/models/two_nodes_gagge.py
  • pythermalcomfort/models/solar_gain.py
  • pythermalcomfort/models/ridge_regression_predict_t_re_t_sk.py
  • pythermalcomfort/models/use_fans_heatwaves.py
  • pythermalcomfort/models/humidex.py
  • pythermalcomfort/models/thi.py
  • pythermalcomfort/models/work_capacity_niosh.py
  • pythermalcomfort/models/pmv_e.py
  • pythermalcomfort/models/vertical_tmp_grad_ppd.py
  • pythermalcomfort/models/heat_index_rothfusz.py
  • pythermalcomfort/models/ankle_draft.py
  • pythermalcomfort/models/cooling_effect.py
  • pythermalcomfort/jos3_functions/construction.py
  • pythermalcomfort/models/two_nodes_gagge_ji.py
  • pythermalcomfort/models/two_nodes_gagge_sleep.py
  • pythermalcomfort/models/wbgt.py
📚 Learning: 2025-09-15T01:23:02.859Z
Learnt from: CR
Repo: CenterForTheBuiltEnvironment/pythermalcomfort PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T01:23:02.859Z
Learning: Applies to pythermalcomfort/models/*.py : Include applicability limits and minimal runnable examples in model function docstrings

Applied to files:

  • pythermalcomfort/utils/scale_wind_speed_log.py
  • pythermalcomfort/models/jos3.py
  • pythermalcomfort/models/at.py
  • pythermalcomfort/jos3_functions/parameters.py
  • pythermalcomfort/jos3_functions/thermoregulation.py
  • pythermalcomfort/utilities.py
  • pythermalcomfort/models/work_capacity_iso.py
  • pythermalcomfort/models/clo_tout.py
  • pythermalcomfort/models/phs.py
  • pythermalcomfort/models/heat_index_lu.py
  • pythermalcomfort/models/two_nodes_gagge.py
  • pythermalcomfort/models/solar_gain.py
  • pythermalcomfort/models/ridge_regression_predict_t_re_t_sk.py
  • pythermalcomfort/models/use_fans_heatwaves.py
  • pythermalcomfort/models/humidex.py
  • pythermalcomfort/models/thi.py
  • pythermalcomfort/models/work_capacity_niosh.py
  • pythermalcomfort/models/pmv_e.py
  • pythermalcomfort/models/vertical_tmp_grad_ppd.py
  • pythermalcomfort/models/pmv_athb.py
  • pythermalcomfort/models/heat_index_rothfusz.py
  • pythermalcomfort/models/ankle_draft.py
  • pythermalcomfort/models/cooling_effect.py
  • pythermalcomfort/jos3_functions/construction.py
  • pythermalcomfort/models/two_nodes_gagge_ji.py
  • pythermalcomfort/models/wbgt.py
📚 Learning: 2025-09-15T01:23:02.859Z
Learnt from: CR
Repo: CenterForTheBuiltEnvironment/pythermalcomfort PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T01:23:02.859Z
Learning: Applies to pythermalcomfort/**/*.py : Functions should be small, single-purpose, and include concise, runnable examples in NumPy-style docstrings

Applied to files:

  • pythermalcomfort/utils/scale_wind_speed_log.py
  • pythermalcomfort/models/jos3.py
  • pythermalcomfort/models/at.py
  • pythermalcomfort/jos3_functions/parameters.py
  • pythermalcomfort/jos3_functions/thermoregulation.py
  • pythermalcomfort/utilities.py
  • pythermalcomfort/models/work_capacity_iso.py
  • pythermalcomfort/models/clo_tout.py
  • pythermalcomfort/jos3_functions/matrix.py
  • pythermalcomfort/models/phs.py
  • pythermalcomfort/models/heat_index_lu.py
  • pythermalcomfort/models/pet_steady.py
  • pythermalcomfort/models/two_nodes_gagge.py
  • pythermalcomfort/models/solar_gain.py
  • pythermalcomfort/models/ridge_regression_predict_t_re_t_sk.py
  • pythermalcomfort/models/use_fans_heatwaves.py
  • pythermalcomfort/models/humidex.py
  • pythermalcomfort/models/thi.py
  • pythermalcomfort/models/work_capacity_niosh.py
  • pythermalcomfort/models/vertical_tmp_grad_ppd.py
  • pythermalcomfort/models/heat_index_rothfusz.py
  • pythermalcomfort/models/ankle_draft.py
  • pythermalcomfort/models/cooling_effect.py
  • pythermalcomfort/jos3_functions/construction.py
  • pythermalcomfort/models/two_nodes_gagge_ji.py
  • pythermalcomfort/models/wbgt.py
📚 Learning: 2025-09-15T01:23:02.859Z
Learnt from: CR
Repo: CenterForTheBuiltEnvironment/pythermalcomfort PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T01:23:02.859Z
Learning: Applies to pythermalcomfort/**/*.py : Use type hints and NumPy-style docstrings for all library code

Applied to files:

  • pythermalcomfort/models/at.py
  • pythermalcomfort/jos3_functions/parameters.py
  • pythermalcomfort/jos3_functions/thermoregulation.py
  • pythermalcomfort/utilities.py
  • pythermalcomfort/models/clo_tout.py
  • pythermalcomfort/jos3_functions/matrix.py
  • pythermalcomfort/models/phs.py
  • pythermalcomfort/models/heat_index_lu.py
  • pythermalcomfort/models/solar_gain.py
  • pythermalcomfort/models/use_fans_heatwaves.py
  • pythermalcomfort/models/humidex.py
  • pythermalcomfort/models/thi.py
  • pythermalcomfort/models/work_capacity_niosh.py
  • pythermalcomfort/models/vertical_tmp_grad_ppd.py
  • pythermalcomfort/models/cooling_effect.py
  • pythermalcomfort/jos3_functions/construction.py
📚 Learning: 2025-09-15T01:23:02.859Z
Learnt from: CR
Repo: CenterForTheBuiltEnvironment/pythermalcomfort PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T01:23:02.859Z
Learning: Applies to pythermalcomfort/**/*.py : Support vectorized behavior; use np.atleast_1d, np.broadcast_to, and check broadcast compatibility

Applied to files:

  • pythermalcomfort/models/at.py
  • pythermalcomfort/utilities.py
  • tests/test_bfu_occupational.py
📚 Learning: 2025-09-15T01:23:02.859Z
Learnt from: CR
Repo: CenterForTheBuiltEnvironment/pythermalcomfort PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T01:23:02.859Z
Learning: Applies to AUTHORS.rst : Optionally add the contributor to AUTHORS.rst

Applied to files:

  • AUTHORS.rst
📚 Learning: 2025-09-15T01:23:02.859Z
Learnt from: CR
Repo: CenterForTheBuiltEnvironment/pythermalcomfort PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T01:23:02.859Z
Learning: Applies to pythermalcomfort/**/*.py : Run ruff (lint/format) and docformatter; ensure code is formatted before PR

Applied to files:

  • pythermalcomfort/jos3_functions/thermoregulation.py
  • pythermalcomfort/utilities.py
  • pythermalcomfort/models/thi.py
  • pythermalcomfort/jos3_functions/construction.py
📚 Learning: 2025-09-15T01:23:02.859Z
Learnt from: CR
Repo: CenterForTheBuiltEnvironment/pythermalcomfort PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T01:23:02.859Z
Learning: Applies to pythermalcomfort/**/*.py : Validate inputs in dataclass __post_init__ (call super().__post_init__() if inheriting BaseInputs)

Applied to files:

  • pythermalcomfort/models/use_fans_heatwaves.py
  • pythermalcomfort/models/humidex.py
  • pythermalcomfort/classes_input.py
  • pythermalcomfort/models/thi.py
📚 Learning: 2025-09-15T01:23:02.859Z
Learnt from: CR
Repo: CenterForTheBuiltEnvironment/pythermalcomfort PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T01:23:02.859Z
Learning: Applies to pythermalcomfort/classes_input.py : Add or update input dataclasses in pythermalcomfort/classes_input.py (or adjacent to the function when appropriate)

Applied to files:

  • pythermalcomfort/classes_input.py
  • pythermalcomfort/classes_return.py
📚 Learning: 2025-09-15T01:23:02.859Z
Learnt from: CR
Repo: CenterForTheBuiltEnvironment/pythermalcomfort PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T01:23:02.859Z
Learning: Applies to pythermalcomfort/models/*.py : Place new domain model functions in pythermalcomfort/models/<name>.py

Applied to files:

  • pythermalcomfort/models/bfu_occupational.py
📚 Learning: 2025-09-15T01:23:02.859Z
Learnt from: CR
Repo: CenterForTheBuiltEnvironment/pythermalcomfort PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T01:23:02.859Z
Learning: Applies to tests/**/*.py : Write tests covering scalars, arrays, broadcasting, and invalid inputs (TypeError/ValueError)

Applied to files:

  • tests/test_bfu_occupational.py
  • tests/test_bfu_rest.py
🧬 Code graph analysis (7)
pythermalcomfort/models/bfu_rest.py (2)
pythermalcomfort/classes_input.py (2)
  • BFU_rest_Groups (21-26)
  • BFU_rest_Inputs (576-610)
pythermalcomfort/classes_return.py (1)
  • BFU_rest (321-345)
pythermalcomfort/classes_input.py (1)
pythermalcomfort/models/jos3.py (10)
  • t_skin (1592-1594)
  • t_skin (1597-1598)
  • tdb (1393-1403)
  • tdb (1406-1407)
  • rh (1449-1451)
  • rh (1454-1455)
  • tr (1410-1412)
  • tr (1415-1416)
  • clo (1494-1496)
  • clo (1499-1500)
pythermalcomfort/models/__init__.py (3)
pythermalcomfort/models/bfu_occupational.py (2)
  • bfu_occupational (9-289)
  • BFU_occupational (292-323)
pythermalcomfort/classes_return.py (2)
  • BFU_occupational (349-388)
  • BFU_rest (321-345)
pythermalcomfort/models/bfu_rest.py (1)
  • BFU_rest (227-240)
pythermalcomfort/classes_return.py (2)
pythermalcomfort/models/bfu_rest.py (1)
  • BFU_rest (227-240)
pythermalcomfort/models/bfu_occupational.py (1)
  • BFU_occupational (292-323)
pythermalcomfort/models/bfu_occupational.py (2)
pythermalcomfort/classes_input.py (1)
  • BFU_occupational_Inputs (614-746)
pythermalcomfort/classes_return.py (1)
  • BFU_occupational (349-388)
tests/test_bfu_occupational.py (2)
pythermalcomfort/models/bfu_occupational.py (2)
  • bfu_occupational (9-289)
  • BFU_occupational (292-323)
pythermalcomfort/classes_return.py (1)
  • BFU_occupational (349-388)
tests/test_bfu_rest.py (2)
pythermalcomfort/classes_return.py (1)
  • BFU_rest (321-345)
pythermalcomfort/models/bfu_rest.py (1)
  • BFU_rest (227-240)
🔇 Additional comments (47)
AUTHORS.rst (1)

18-18: LGTM! Contributor addition follows existing patterns.

The contributor entry and corresponding link anchor are properly formatted and consistent with the repository's attribution structure.

Also applies to: 26-26

pythermalcomfort/models/pmv_e.py (1)

23-29: LGTM! Docstring formatting refinement.

This is a cosmetic line-wrapping adjustment with no functional impact. Aligns with the PR's automated linting/formatting pass.

pythermalcomfort/models/at.py (1)

17-22: LGTM! Formatting refinement.

Line-wrapping adjustment to the docstring with no functional changes. Consistent with the automated formatting applied across the PR.

pythermalcomfort/models/heat_index_rothfusz.py (1)

16-17: LGTM! Docstring formatting adjustment.

Line-wrapping refinement with no functional impact. Consistent with the automated formatting pass.

pythermalcomfort/models/work_capacity_niosh.py (1)

13-14: LGTM! Formatting refinement.

Docstring line-wrapping adjustment with no functional changes. Part of the automated formatting applied in this PR.

pythermalcomfort/models/ankle_draft.py (1)

26-27: LGTM! Formatting adjustment.

Line-wrapping refinement to the docstring with no functional impact. Consistent with the PR's automated formatting.

pythermalcomfort/models/pmv_a.py (1)

25-29: LGTM! Docstring formatting refinement.

Line-wrapping adjustment with no functional changes. Aligns with the automated formatting applied across the PR.

pythermalcomfort/utilities.py (3)

818-819: LGTM – formatting improvement.

The docstring reflow is clean and consistent with the repository's style guidelines.


1016-1017: LGTM – formatting improvement.

The docstring adjustment improves readability without changing semantics.


1060-1061: LGTM – formatting improvement.

The docstring formatting is consistent with the repository's standards.

pythermalcomfort/models/pmv_athb.py (1)

20-26: LGTM – docstring formatting improvement.

The reformatted docstring maintains clarity while adhering to the repository's style guidelines. No functional changes detected.

pythermalcomfort/models/cooling_effect.py (1)

25-30: LGTM – docstring formatting improvement.

The reformatted docstring is clearer and consistent with the repository's formatting standards. No functional changes.

pythermalcomfort/models/two_nodes_gagge_ji.py (1)

25-26: LGTM – docstring formatting improvement.

The docstring reflow is clean and maintains the intended meaning without any functional changes.

pythermalcomfort/models/thi.py (1)

14-15: LGTM – docstring formatting improvement.

The reformatted docstring is consistent with the repository's style guidelines. No functional changes.

pythermalcomfort/models/clo_tout.py (1)

16-17: LGTM – docstring formatting improvement.

The docstring adjustment is clean and maintains clarity. No functional changes detected.

pythermalcomfort/models/vertical_tmp_grad_ppd.py (1)

21-23: LGTM – docstring formatting improvement.

The docstring reflow improves readability while maintaining the original meaning. No functional changes.

pythermalcomfort/models/jos3.py (3)

382-383: LGTM – docstring formatting improvement.

The docstring reflow is clean and consistent with the repository's style guidelines. No functional changes.


610-611: LGTM – docstring formatting improvement.

The reformatted docstring maintains clarity and adheres to the repository's standards.


1139-1140: LGTM – docstring formatting improvement.

The docstring adjustment improves readability without changing semantics. Formatting is consistent with the repository's guidelines.

pythermalcomfort/models/heat_index_lu.py (1)

16-42: Docstring tidy-up looks good.
Reflow aligns with the NumPy-style guidance and keeps the summary crisp—no behavioural risk introduced.

pythermalcomfort/models/work_capacity_iso.py (1)

13-44: Docstring reflow approved.
The formatting tweak keeps the prose readable while maintaining reference details—nothing further needed.

pythermalcomfort/models/phs.py (1)

32-202: Docstring polish looks solid.
The updated opening paragraph reads cleanly and stays faithful to the ISO references—no issues spotted.

pythermalcomfort/jos3_functions/matrix.py (1)

1-168: Docstring refresh acknowledged.
The concise module summary matches our documentation tone and introduces no functional churn.

pythermalcomfort/models/wbgt.py (1)

17-69: Docstring formatting change LGTM.
The reshaped intro adheres to our doc formatter style, keeping the WBGT guidance intact.

pythermalcomfort/models/humidex.py (1)

16-20: LGTM - Formatting changes only.

The docstring reflow improves readability without affecting functionality.

pythermalcomfort/jos3_functions/construction.py (1)

1-6: LGTM - Docstring formatting improvements.

Module docstring reflow enhances readability with no functional changes.

pythermalcomfort/models/pet_steady.py (1)

234-236: LGTM - Docstring formatting adjustment.

The docstring reflow improves line-length consistency without affecting the implementation.

pythermalcomfort/models/solar_gain.py (1)

24-29: LGTM - Docstring formatting enhancement.

Line wrapping adjustment improves readability with no functional impact.

pythermalcomfort/models/use_fans_heatwaves.py (1)

32-32: LGTM - Docstring formatting improvement.

The opening sentence consolidation enhances consistency without affecting functionality.

pythermalcomfort/models/two_nodes_gagge_sleep.py (1)

22-23: LGTM - Docstring formatting adjustment.

The line reflow improves consistency with no functional changes.

pythermalcomfort/models/ridge_regression_predict_t_re_t_sk.py (2)

3-5: LGTM - Module docstring formatting improvement.

The line wrapping adjustment enhances readability without affecting functionality.


206-207: LGTM - Function docstring formatting adjustment.

The docstring reflow improves consistency with no functional changes.

pythermalcomfort/models/two_nodes_gagge.py (1)

30-31: LGTM - Docstring formatting enhancement.

The line reflow improves readability without affecting the implementation.

pythermalcomfort/models/__init__.py (1)

5-6: LGTM! Public API exports are correctly structured.

The new biophysical fan use models are properly imported and exposed following the repository's established patterns. Both imports and __all__ entries maintain alphabetical ordering.

Also applies to: 47-48

CHANGELOG.rst (1)

4-8: LGTM! Changelog entry is clear and properly formatted.

The version entry accurately documents the addition of the two biophysical fan use models with appropriate reference citations. The RST syntax is correct.

pythermalcomfort/jos3_functions/parameters.py (3)

1-14: LGTM! Formatting improvements only.

Module docstring has been reformatted for better readability. No functional changes.


420-420: LGTM! Formatting improvement only.

Docstring line rewrapped for better readability. No functional changes.


457-458: LGTM! Formatting improvement only.

Docstring line rewrapped for better readability. No functional changes.

pythermalcomfort/utils/scale_wind_speed_log.py (1)

17-18: LGTM! Formatting improvement only.

Docstring first line rewrapped for better readability. No functional changes.

pythermalcomfort/jos3_functions/thermoregulation.py (1)

1-1: LGTM! Formatting improvement only.

Module docstring reformatted. No functional changes.

docs/documentation/references.rst (1)

45-46: LGTM! Reference entries are properly formatted.

Both Morris2021 and Foster2021 references are correctly added with appropriate bibliographic information, matching the citations used in the new models' documentation.

tests/test_bfu_occupational.py (3)

7-26: LGTM! Scalar test validates core functionality.

Test properly validates:

  • Heat storage reduction with fan use
  • Interpretation mapping based on delta_storage thresholds
  • Use of round_output=False for precise assertions

29-64: LGTM! Array test validates vectorization.

Test properly validates:

  • All output attributes are numpy arrays with correct shapes
  • Broadcasting works with per-element position and activity arrays
  • Interpretation logic applied element-wise

67-73: LGTM! Error handling validated.

Test correctly verifies that an invalid position ("lying") raises a ValueError, ensuring proper input validation.

tests/test_bfu_rest.py (3)

7-11: LGTM! Scalar test validates core functionality.

Test properly validates:

  • Group identifier propagation to result
  • Positive heat_storage indicating fan benefit

14-28: LGTM! Array test validates vectorization.

Test properly validates:

  • All output attributes are numpy arrays with correct shapes
  • Vectorized computation works correctly
  • Use of round_output=False for precise assertions

31-33: LGTM! Error handling validated.

Test correctly verifies that an invalid group raises a ValueError, ensuring proper input validation.

@FedericoTartarini
Copy link
Collaborator

@nravanelli thank you so much for working on this, could you please review the comments from coderabbit. I will then also provide comments to the code.

Copy link

@KristinaM93 KristinaM93 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have left a few comments on the classes_input.py

@FedericoTartarini FedericoTartarini changed the base branch from master to development November 5, 2025 04:29
@FedericoTartarini
Copy link
Collaborator

@nravanelli thank you for contributing to the project. I noticed the following issues:

  • you did not pull the latest changes to development so it looks like you have changed 54 files which I am assuming you did not modify. Could you please pull the latest changes and update the pull request? I will then review your code.
  • the pull request should have been open targeting development not master. I fixed that.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ed4d584 and d68081a.

📒 Files selected for processing (5)
  • .pre-commit-config.yaml (1 hunks)
  • CONTRIBUTING.rst (1 hunks)
  • pythermalcomfort/classes_input.py (2 hunks)
  • pythermalcomfort/models/bfu_rest.py (1 hunks)
  • tests/test_bfu_occupational.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
pythermalcomfort/**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

pythermalcomfort/**/*.py: Use type hints and NumPy-style docstrings for all library code
Validate inputs in dataclass post_init (call super().post_init() if inheriting BaseInputs)
Convert pandas Series to list/np.array and normalize numeric inputs with np.asarray in post_init
Use validate_type(...) for type checks; raise TypeError for wrong types and ValueError for invalid values
Enforce physical constraints in validation (e.g., non-negativity, z2>z0, z1>z0, z2>z1) with clear error messages
Use numpy operations for numerical work (e.g., np.log, np.asarray) and avoid math.log when arrays are accepted
Validate domains before logs/roots (arguments must be > 0)
Support vectorized behavior; use np.atleast_1d, np.broadcast_to, and check broadcast compatibility
Follow repository patterns: dataclasses, BaseInputs metadata, validate_type, Units/Enums (Postures, WorkIntensity, Sex)
Functions should be small, single-purpose, and include concise, runnable examples in NumPy-style docstrings
Run ruff (lint/format) and docformatter; ensure code is formatted before PR
When accepting arrays, ensure computations are vectorized and avoid per-element Python loops

Files:

  • pythermalcomfort/classes_input.py
  • pythermalcomfort/models/bfu_rest.py
pythermalcomfort/classes_input.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Add or update input dataclasses in pythermalcomfort/classes_input.py (or adjacent to the function when appropriate)

Files:

  • pythermalcomfort/classes_input.py
pythermalcomfort/models/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

pythermalcomfort/models/*.py: Place new domain model functions in pythermalcomfort/models/.py
Include applicability limits and minimal runnable examples in model function docstrings

Files:

  • pythermalcomfort/models/bfu_rest.py
tests/**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

tests/**/*.py: Write tests covering scalars, arrays, broadcasting, and invalid inputs (TypeError/ValueError)
Use numpy.testing or pytest.approx for numeric assertions
Ensure tests are deterministic, small, and pass locally with pytest before PR

Files:

  • tests/test_bfu_occupational.py
🧠 Learnings (11)
📓 Common learnings
Learnt from: CR
Repo: CenterForTheBuiltEnvironment/pythermalcomfort PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T01:23:02.859Z
Learning: Applies to pythermalcomfort/**/*.py : Follow repository patterns: dataclasses, BaseInputs metadata, validate_type, Units/Enums (Postures, WorkIntensity, Sex)
📚 Learning: 2025-09-15T01:23:02.859Z
Learnt from: CR
Repo: CenterForTheBuiltEnvironment/pythermalcomfort PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T01:23:02.859Z
Learning: Applies to pythermalcomfort/**/*.py : Follow repository patterns: dataclasses, BaseInputs metadata, validate_type, Units/Enums (Postures, WorkIntensity, Sex)

Applied to files:

  • pythermalcomfort/classes_input.py
  • CONTRIBUTING.rst
  • tests/test_bfu_occupational.py
📚 Learning: 2025-09-15T01:23:02.859Z
Learnt from: CR
Repo: CenterForTheBuiltEnvironment/pythermalcomfort PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T01:23:02.859Z
Learning: Applies to pythermalcomfort/classes_input.py : Add or update input dataclasses in pythermalcomfort/classes_input.py (or adjacent to the function when appropriate)

Applied to files:

  • pythermalcomfort/classes_input.py
📚 Learning: 2025-09-15T01:23:02.859Z
Learnt from: CR
Repo: CenterForTheBuiltEnvironment/pythermalcomfort PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T01:23:02.859Z
Learning: Applies to pythermalcomfort/**/*.py : Validate inputs in dataclass __post_init__ (call super().__post_init__() if inheriting BaseInputs)

Applied to files:

  • pythermalcomfort/classes_input.py
📚 Learning: 2025-09-15T01:23:02.859Z
Learnt from: CR
Repo: CenterForTheBuiltEnvironment/pythermalcomfort PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T01:23:02.859Z
Learning: Applies to tests/**/*.py : Write tests covering scalars, arrays, broadcasting, and invalid inputs (TypeError/ValueError)

Applied to files:

  • pythermalcomfort/classes_input.py
  • tests/test_bfu_occupational.py
📚 Learning: 2025-09-15T01:23:02.859Z
Learnt from: CR
Repo: CenterForTheBuiltEnvironment/pythermalcomfort PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T01:23:02.859Z
Learning: Applies to pythermalcomfort/**/*.py : Run ruff (lint/format) and docformatter; ensure code is formatted before PR

Applied to files:

  • CONTRIBUTING.rst
📚 Learning: 2025-09-15T01:23:02.859Z
Learnt from: CR
Repo: CenterForTheBuiltEnvironment/pythermalcomfort PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T01:23:02.859Z
Learning: Add a PR checklist including tests, docstrings, docs, CHANGELOG (if applicable), lint/format, and CI checks

Applied to files:

  • CONTRIBUTING.rst
📚 Learning: 2025-09-15T01:23:02.859Z
Learnt from: CR
Repo: CenterForTheBuiltEnvironment/pythermalcomfort PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T01:23:02.859Z
Learning: Applies to pythermalcomfort/**/*.py : Use type hints and NumPy-style docstrings for all library code

Applied to files:

  • CONTRIBUTING.rst
📚 Learning: 2025-09-15T01:23:02.859Z
Learnt from: CR
Repo: CenterForTheBuiltEnvironment/pythermalcomfort PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T01:23:02.859Z
Learning: Applies to pythermalcomfort/{models,utilities}.py : Ensure docstring sections include Args (with units), Returns, Raises, Examples, Applicability

Applied to files:

  • CONTRIBUTING.rst
📚 Learning: 2025-09-15T01:23:02.859Z
Learnt from: CR
Repo: CenterForTheBuiltEnvironment/pythermalcomfort PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T01:23:02.859Z
Learning: Applies to pythermalcomfort/**/*.py : Functions should be small, single-purpose, and include concise, runnable examples in NumPy-style docstrings

Applied to files:

  • CONTRIBUTING.rst
📚 Learning: 2025-09-15T01:23:02.859Z
Learnt from: CR
Repo: CenterForTheBuiltEnvironment/pythermalcomfort PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T01:23:02.859Z
Learning: Applies to pythermalcomfort/**/*.py : Support vectorized behavior; use np.atleast_1d, np.broadcast_to, and check broadcast compatibility

Applied to files:

  • tests/test_bfu_occupational.py
🧬 Code graph analysis (2)
pythermalcomfort/models/bfu_rest.py (2)
pythermalcomfort/classes_input.py (2)
  • BFU_rest_Groups (21-34)
  • BFU_rest_Inputs (584-618)
pythermalcomfort/classes_return.py (1)
  • BFU_rest (321-345)
tests/test_bfu_occupational.py (2)
pythermalcomfort/models/bfu_occupational.py (2)
  • bfu_occupational (9-289)
  • BFU_occupational (292-323)
pythermalcomfort/classes_return.py (1)
  • BFU_occupational (349-388)

…i/Feature/biophysical_fan_models

# Conflicts:
#	CHANGELOG.rst
#	docs/documentation/references.rst
#	pythermalcomfort/utilities.py
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c323f04 and 88d831c.

📒 Files selected for processing (3)
  • CHANGELOG.rst (1 hunks)
  • docs/documentation/references.rst (1 hunks)
  • pythermalcomfort/classes_return.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
CHANGELOG.*

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Update the CHANGELOG for public API changes

Files:

  • CHANGELOG.rst
docs/**/*.rst

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

docs/**/*.rst: Add an '.. autofunction:: pythermalcomfort.models..' entry to the docs for public APIs
Update the appropriate docs reference file that aggregates API docs when adding a public function

Files:

  • docs/documentation/references.rst
pythermalcomfort/**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

pythermalcomfort/**/*.py: Use type hints and NumPy-style docstrings for all library code
Validate inputs in dataclass post_init (call super().post_init() if inheriting BaseInputs)
Convert pandas Series to list/np.array and normalize numeric inputs with np.asarray in post_init
Use validate_type(...) for type checks; raise TypeError for wrong types and ValueError for invalid values
Enforce physical constraints in validation (e.g., non-negativity, z2>z0, z1>z0, z2>z1) with clear error messages
Use numpy operations for numerical work (e.g., np.log, np.asarray) and avoid math.log when arrays are accepted
Validate domains before logs/roots (arguments must be > 0)
Support vectorized behavior; use np.atleast_1d, np.broadcast_to, and check broadcast compatibility
Follow repository patterns: dataclasses, BaseInputs metadata, validate_type, Units/Enums (Postures, WorkIntensity, Sex)
Functions should be small, single-purpose, and include concise, runnable examples in NumPy-style docstrings
Run ruff (lint/format) and docformatter; ensure code is formatted before PR
When accepting arrays, ensure computations are vectorized and avoid per-element Python loops

Files:

  • pythermalcomfort/classes_return.py
🧠 Learnings (2)
📚 Learning: 2025-09-15T01:23:02.859Z
Learnt from: CR
Repo: CenterForTheBuiltEnvironment/pythermalcomfort PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T01:23:02.859Z
Learning: Applies to pythermalcomfort/**/*.py : Follow repository patterns: dataclasses, BaseInputs metadata, validate_type, Units/Enums (Postures, WorkIntensity, Sex)

Applied to files:

  • pythermalcomfort/classes_return.py
📚 Learning: 2025-09-15T01:23:02.859Z
Learnt from: CR
Repo: CenterForTheBuiltEnvironment/pythermalcomfort PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T01:23:02.859Z
Learning: Applies to pythermalcomfort/classes_input.py : Add or update input dataclasses in pythermalcomfort/classes_input.py (or adjacent to the function when appropriate)

Applied to files:

  • pythermalcomfort/classes_return.py
🧬 Code graph analysis (1)
pythermalcomfort/classes_return.py (2)
pythermalcomfort/models/bfu_rest.py (1)
  • BFU_rest (225-238)
pythermalcomfort/models/bfu_occupational.py (1)
  • BFU_occupational (292-323)
🔇 Additional comments (2)
CHANGELOG.rst (1)

4-8: CHANGELOG entry is clear and follows repository conventions.

The new version entry correctly documents the addition of the two biophysical fan use models with proper references. The brief format is consistent with other entries in the changelog. The date matches the release context, and the reference notation [Morris2021]_ and [Foster2021]_ correctly links to the new references added in docs/documentation/references.rst.

docs/documentation/references.rst (1)

45-46: Consider adding DOIs for the new references.

The reference format is consistent with existing entries, but many recent references in this file include DOI links (e.g., Forbes2025, Ji2022, Yan2025). Both Morris2021 and Foster2021 should have DOIs that would improve traceability.

Based on the PR description mentioning specific DOIs, please verify and add them to the references:

  • Morris et al. 2021: DOI: 10.1016/S2542-5196(21)00136-4
  • Foster et al. 2021: DOI: 10.1007/s00484-021-02212-y

Apply this diff to add the DOIs:

-.. [Morris2021] Morris N, Chaseling G, English T, Gruss F, Bin Maideen MF, Capon A, Jay O. Electric fan use for cooling during hot weather: a biophysical modelling study. Lancet - Planetary Health. 2021; 5(6): E368-E377.
-.. [Foster2021] Foster J, Smallcombe J, Hodder S, Jay O, Flouris A, Havenith G. Quantifying the impact of heat on human physical work capacity; part II: the observed interaction of air velocity with temperature, humidity, sweat rate, and clothing is not captured by most heat stress indices. International Journal of Biometeorology. 2021 Nov;66(3):507-520.
+.. [Morris2021] Morris N, Chaseling G, English T, Gruss F, Bin Maideen MF, Capon A, Jay O. Electric fan use for cooling during hot weather: a biophysical modelling study. Lancet - Planetary Health. 2021; 5(6): E368-E377. DOI: 10.1016/S2542-5196(21)00136-4
+.. [Foster2021] Foster J, Smallcombe J, Hodder S, Jay O, Flouris A, Havenith G. Quantifying the impact of heat on human physical work capacity; part II: the observed interaction of air velocity with temperature, humidity, sweat rate, and clothing is not captured by most heat stress indices. International Journal of Biometeorology. 2021 Nov;66(3):507-520. DOI: 10.1007/s00484-021-02212-y

Comment on lines +321 to +347
@dataclass(frozen=True, repr=False)
class BFU_rest(AutoStrMixin):
"""Dataclass for the fan biophysical model from Morris et al. 2021.
Attributes
----------
heat_storage : float or list of floats
Differential heat storage when using the fan vs. not using the fan, [W/m²].
e_req_fan : float or list of floats
Required evaporative heat loss with the fan, [W/m²].
e_max_fan : float or list of floats
Maximum possible evaporative heat loss with the fan, [W/m²].
e_req_no_fan : float or list of floats
Required evaporative heat loss without the fan, [W/m²].
e_max_no_fan : float or list of floats
Maximum possible evaporative heat loss without the fan, [W/m²].
group : str
Population group identifier used for the calculation (YNG, OLD, MEDS).
"""

heat_storage: float | list[float]
e_req_fan: float | list[float]
e_max_fan: float | list[float]
e_req_no_fan: float | list[float]
e_max_no_fan: float | list[float]
group: str

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Type hints should include np.ndarray for vectorized support.

The type hints for numeric fields use float | list[float], but the models support vectorized operations via NumPy arrays. For consistency with the coding guidelines ("Support vectorized behavior; use np.atleast_1d, np.broadcast_to") and to accurately reflect the implementation, consider using float | list[float] | np.ndarray or npt.ArrayLike for all numeric fields.

This pattern is already used in some dataclasses (e.g., HI at line 301, ScaleWindSpeedLog at line 1117), though many legacy dataclasses don't include it. For new code, explicitly supporting np.ndarray in type hints improves clarity.

Apply this diff to update the type hints:

 @dataclass(frozen=True, repr=False)
 class BFU_rest(AutoStrMixin):
     """Dataclass for the fan biophysical model from Morris et al. 2021.
 
     Attributes
     ----------
-    heat_storage : float or list of floats
+    heat_storage : float or list of floats or np.ndarray
         Differential heat storage when using the fan vs. not using the fan, [W/m²].
-    e_req_fan : float or list of floats
+    e_req_fan : float or list of floats or np.ndarray
         Required evaporative heat loss with the fan, [W/m²].
-    e_max_fan : float or list of floats
+    e_max_fan : float or list of floats or np.ndarray
         Maximum possible evaporative heat loss with the fan, [W/m²].
-    e_req_no_fan : float or list of floats
+    e_req_no_fan : float or list of floats or np.ndarray
         Required evaporative heat loss without the fan, [W/m²].
-    e_max_no_fan : float or list of floats
+    e_max_no_fan : float or list of floats or np.ndarray
         Maximum possible evaporative heat loss without the fan, [W/m²].
     group : str
         Population group identifier used for the calculation (YNG, OLD, MEDS).
     """
 
-    heat_storage: float | list[float]
-    e_req_fan: float | list[float]
-    e_max_fan: float | list[float]
-    e_req_no_fan: float | list[float]
-    e_max_no_fan: float | list[float]
+    heat_storage: float | list[float] | np.ndarray
+    e_req_fan: float | list[float] | np.ndarray
+    e_max_fan: float | list[float] | np.ndarray
+    e_req_no_fan: float | list[float] | np.ndarray
+    e_max_no_fan: float | list[float] | np.ndarray
     group: str
🤖 Prompt for AI Agents
In pythermalcomfort/classes_return.py around lines 321 to 347, the numeric type
hints for BFU_rest use float | list[float] but should include NumPy array types
for vectorized support; update the type annotations for heat_storage, e_req_fan,
e_max_fan, e_req_no_fan, and e_max_no_fan to include np.ndarray or, preferably,
npt.ArrayLike (e.g., float | list[float] | npt.ArrayLike) and add the necessary
import from numpy.typing (import numpy.typing as npt) or import numpy as np if
you opt for np.ndarray, ensuring consistency with other dataclasses that support
vectorized inputs.

Comment on lines +349 to +390
@dataclass(frozen=True, repr=False)
class BFU_occupational(AutoStrMixin):
"""Dataclass for the occupational fan use model from Foster et al. 2021.
Attributes
----------
storage_fan : float or list of floats
Heat storage rate when the fan is operating, [W/m²].
storage_no_fan : float or list of floats
Heat storage rate without the fan, [W/m²].
delta_storage : float or list of floats
Reduction in heat storage provided by the fan (positive means beneficial), [W/m²].
dry_heat_fan : float or list of floats
Dry heat loss with the fan (radiative + convective), [W/m²].
dry_heat_no_fan : float or list of floats
Dry heat loss without the fan, [W/m²].
evaporative_heat_fan : float or list of floats
Evaporative heat loss via sweating with the fan, [W/m²].
evaporative_heat_no_fan : float or list of floats
Evaporative heat loss via sweating without the fan, [W/m²].
respiratory_heat : float or list of floats
Combined sensible and latent respiratory heat exchange, [W/m²].
sweat_efficiency_fan : float or list of floats
Sweating efficiency (η) with the fan, [-].
sweat_efficiency_no_fan : float or list of floats
Sweating efficiency (η) without the fan, [-].
interpretation : int or list of int
Qualitative interpretation based on Foster et al 2021 (1 = beneficial, 0 = neutral, -1 = harmful).
"""

storage_fan: float | list[float]
storage_no_fan: float | list[float]
delta_storage: float | list[float]
dry_heat_fan: float | list[float]
dry_heat_no_fan: float | list[float]
evaporative_heat_fan: float | list[float]
evaporative_heat_no_fan: float | list[float]
respiratory_heat: float | list[float]
sweat_efficiency_fan: float | list[float]
sweat_efficiency_no_fan: float | list[float]
interpretation: int | list[int]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Type hints should include np.ndarray for vectorized support.

Similar to BFU_rest, the type hints for numeric and integer fields should include np.ndarray to accurately reflect vectorized operation support. This applies to all fields including interpretation, which should be int | list[int] | np.ndarray.

Apply this diff to update the type hints:

 @dataclass(frozen=True, repr=False)
 class BFU_occupational(AutoStrMixin):
     """Dataclass for the occupational fan use model from Foster et al. 2021.
 
     Attributes
     ----------
-    storage_fan : float or list of floats
+    storage_fan : float or list of floats or np.ndarray
         Heat storage rate when the fan is operating, [W/m²].
-    storage_no_fan : float or list of floats
+    storage_no_fan : float or list of floats or np.ndarray
         Heat storage rate without the fan, [W/m²].
-    delta_storage : float or list of floats
+    delta_storage : float or list of floats or np.ndarray
         Reduction in heat storage provided by the fan (positive means beneficial), [W/m²].
-    dry_heat_fan : float or list of floats
+    dry_heat_fan : float or list of floats or np.ndarray
         Dry heat loss with the fan (radiative + convective), [W/m²].
-    dry_heat_no_fan : float or list of floats
+    dry_heat_no_fan : float or list of floats or np.ndarray
         Dry heat loss without the fan, [W/m²].
-    evaporative_heat_fan : float or list of floats
+    evaporative_heat_fan : float or list of floats or np.ndarray
         Evaporative heat loss via sweating with the fan, [W/m²].
-    evaporative_heat_no_fan : float or list of floats
+    evaporative_heat_no_fan : float or list of floats or np.ndarray
         Evaporative heat loss via sweating without the fan, [W/m²].
-    respiratory_heat : float or list of floats
+    respiratory_heat : float or list of floats or np.ndarray
         Combined sensible and latent respiratory heat exchange, [W/m²].
-    sweat_efficiency_fan : float or list of floats
+    sweat_efficiency_fan : float or list of floats or np.ndarray
         Sweating efficiency (η) with the fan, [-].
-    sweat_efficiency_no_fan : float or list of floats
+    sweat_efficiency_no_fan : float or list of floats or np.ndarray
         Sweating efficiency (η) without the fan, [-].
-    interpretation : int or list of int
+    interpretation : int or list of int or np.ndarray
         Qualitative interpretation based on Foster et al 2021 (1 = beneficial, 0 = neutral, -1 = harmful).
     """
 
-    storage_fan: float | list[float]
-    storage_no_fan: float | list[float]
-    delta_storage: float | list[float]
-    dry_heat_fan: float | list[float]
-    dry_heat_no_fan: float | list[float]
-    evaporative_heat_fan: float | list[float]
-    evaporative_heat_no_fan: float | list[float]
-    respiratory_heat: float | list[float]
-    sweat_efficiency_fan: float | list[float]
-    sweat_efficiency_no_fan: float | list[float]
-    interpretation: int | list[int]
+    storage_fan: float | list[float] | np.ndarray
+    storage_no_fan: float | list[float] | np.ndarray
+    delta_storage: float | list[float] | np.ndarray
+    dry_heat_fan: float | list[float] | np.ndarray
+    dry_heat_no_fan: float | list[float] | np.ndarray
+    evaporative_heat_fan: float | list[float] | np.ndarray
+    evaporative_heat_no_fan: float | list[float] | np.ndarray
+    respiratory_heat: float | list[float] | np.ndarray
+    sweat_efficiency_fan: float | list[float] | np.ndarray
+    sweat_efficiency_no_fan: float | list[float] | np.ndarray
+    interpretation: int | list[int] | np.ndarray
🤖 Prompt for AI Agents
In pythermalcomfort/classes_return.py around lines 349 to 390, the dataclass
BFU_occupational's type hints currently list float | list[float] and int |
list[int] but omit np.ndarray support; update every numeric and integer field
annotation (storage_fan, storage_no_fan, delta_storage, dry_heat_fan,
dry_heat_no_fan, evaporative_heat_fan, evaporative_heat_no_fan,
respiratory_heat, sweat_efficiency_fan, sweat_efficiency_no_fan, interpretation)
to include np.ndarray in the union (e.g., float | list[float] | np.ndarray and
int | list[int] | np.ndarray for interpretation), and ensure numpy is imported
as np at the top of the module if it isn’t already.

@FedericoTartarini FedericoTartarini self-requested a review November 12, 2025 04:15
Copy link
Collaborator

@FedericoTartarini FedericoTartarini left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you Nick so much for contributing, I left a few important comments for you to review and address. I also merged development in this branch to make sure that the changes in this pull request are only related to the models you are proposing to include. Please let me know if my comments are not clear, happy to have a quick chat if needed.

:members:

Biophysical Fan Use Model: Occupational
-------------------------
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you please describe briefly what these model are and why there are two, occupational and resting? Should not be met an input and should not we combine the two?


This function is derived from the occupational biophysical fan use model described by Foster et al. 2021 [Foster2021]_

.. autofunction:: pythermalcomfort.models.bfu_occupational.BFU_occupational
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all functions names should be lower case

Biophysical Fan Use Model: Occupational
-------------------------

This function is derived from the occupational biophysical fan use model described by Foster et al. 2021 [Foster2021]_
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this text and move it inside the function docstring

body_surface_area: float | list[float] | np.ndarray = 1.81,
round_output: bool = True,
) -> BFUOccupationalResult:
"""Compute the potential benefit of fans in an occupational context using the
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please provide more information about the model and include the reference

tdb: float | list[float] | np.ndarray,
rh: float | list[float] | np.ndarray,
tr: float | list[float] | np.ndarray | None = None,
clo: float | list[float] | np.ndarray = 0.28,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clo and met should not be default values, the user should enter them manually



@dataclass(frozen=True, repr=False)
class BFU_rest(AutoStrMixin):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check name

e_max_fan: float | list[float]
e_req_no_fan: float | list[float]
e_max_no_fan: float | list[float]
group: str
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure this should be returned since it is an input and it is not modified by the function, I assume

respiratory_heat: float | list[float]
sweat_efficiency_fan: float | list[float]
sweat_efficiency_no_fan: float | list[float]
interpretation: int | list[int]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we use a better variable name?

delta_storage: float | list[float]
dry_heat_fan: float | list[float]
dry_heat_no_fan: float | list[float]
evaporative_heat_fan: float | list[float]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the var names do not follow the same naming convention in the new two return classes

assert result.delta_storage < 0 # fan reduces heat storage
assert result.storage_no_fan > result.storage_fan

if result.delta_storage < -50:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we will need to add a few more tests if possible

@FedericoTartarini
Copy link
Collaborator

@nravanelli please also review the comments from coderabbit and check the code with ruff check and format

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants