-
Notifications
You must be signed in to change notification settings - Fork 10
feat: #1139 - [E5-F3-P1] Create CondensationLatentHeat class skeleton with tests #1146
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -38,6 +38,10 @@ | |
| ) | ||
| from particula.gas import get_molecule_mean_free_path | ||
| from particula.gas.gas_data import GasData | ||
| from particula.gas.latent_heat_strategies import ( | ||
| ConstantLatentHeat, | ||
| LatentHeatStrategy, | ||
| ) | ||
| from particula.gas.species import GasSpecies | ||
| from particula.gas.vapor_pressure_strategies import VaporPressureStrategy | ||
| from particula.particles import ( | ||
|
|
@@ -1696,3 +1700,190 @@ def step( # noqa: C901 | |
| (particle, gas_species), | ||
| ) | ||
| return cast(Tuple[ParticleData, GasData], (particle_data, gas_data)) | ||
|
|
||
|
|
||
| class CondensationLatentHeat(CondensationStrategy): | ||
| """Condensation strategy with latent heat configuration. | ||
|
|
||
| This class mirrors the base condensation setup while deferring the | ||
| non-isothermal mass-transfer implementation to later phases. It resolves | ||
| latent heat either from a provided strategy or a scalar fallback value. | ||
|
|
||
| Attributes: | ||
| latent_heat_strategy_input: Strategy input provided at initialization. | ||
| latent_heat_input: Raw latent heat value provided at initialization. | ||
| last_latent_heat_energy: Diagnostic latent heat energy tracker. | ||
| """ | ||
|
|
||
| # pylint: disable=R0913, R0917 | ||
| def __init__( | ||
| self, | ||
| molar_mass: Union[float, NDArray[np.float64]], | ||
| diffusion_coefficient: Union[float, NDArray[np.float64]] = 2e-5, | ||
| accommodation_coefficient: Union[float, NDArray[np.float64]] = 1.0, | ||
| update_gases: bool = True, | ||
| skip_partitioning_indices: Optional[Sequence[int]] = None, | ||
| activity_strategy: ActivityStrategy | None = None, | ||
| surface_strategy: SurfaceStrategy | None = None, | ||
| vapor_pressure_strategy: VaporPressureStrategy | ||
| | Sequence[VaporPressureStrategy] | ||
| | None = None, | ||
| *, | ||
| latent_heat_strategy: LatentHeatStrategy | None = None, | ||
| latent_heat: float | NDArray[np.float64] = 0.0, | ||
| ): | ||
| """Initialize the CondensationLatentHeat strategy. | ||
|
|
||
| Args: | ||
| molar_mass: Molar mass of the species [kg/mol]. | ||
| diffusion_coefficient: Diffusion coefficient [m^2/s]. | ||
| accommodation_coefficient: Mass accommodation coefficient. | ||
| update_gases: Whether to update gas concentrations on update. | ||
| skip_partitioning_indices: Species indices that should skip | ||
| partitioning. | ||
| activity_strategy: Activity strategy used for ParticleData inputs. | ||
| surface_strategy: Surface strategy used for ParticleData inputs. | ||
| vapor_pressure_strategy: Vapor pressure strategy used for GasData | ||
| inputs. | ||
| latent_heat_strategy: Optional latent heat strategy to use. | ||
| latent_heat: Scalar latent heat fallback [J/kg]. | ||
| """ | ||
| super().__init__( | ||
| molar_mass=molar_mass, | ||
| diffusion_coefficient=diffusion_coefficient, | ||
| accommodation_coefficient=accommodation_coefficient, | ||
| update_gases=update_gases, | ||
| skip_partitioning_indices=skip_partitioning_indices, | ||
| activity_strategy=activity_strategy, | ||
| surface_strategy=surface_strategy, | ||
| vapor_pressure_strategy=vapor_pressure_strategy, | ||
| ) | ||
| self.latent_heat_strategy_input = latent_heat_strategy | ||
| self.latent_heat_input = latent_heat | ||
| self._latent_heat_strategy = self._resolve_latent_heat_strategy( | ||
| latent_heat_strategy=latent_heat_strategy, | ||
| latent_heat=latent_heat, | ||
| ) | ||
| self.last_latent_heat_energy = 0.0 | ||
|
|
||
| def _resolve_latent_heat_strategy( | ||
| self, | ||
| latent_heat_strategy: LatentHeatStrategy | None, | ||
| latent_heat: float | NDArray[np.float64], | ||
| ) -> LatentHeatStrategy | None: | ||
| """Resolve the latent heat strategy from inputs. | ||
|
|
||
| Prefers an explicit strategy, otherwise converts scalar latent heat | ||
| values into a constant strategy. Array-like or negative latent heat | ||
| values log a warning and fall back to isothermal behavior. | ||
|
|
||
| Args: | ||
| latent_heat_strategy: Optional strategy to use directly. | ||
| latent_heat: Scalar or array-like latent heat input [J/kg]. | ||
|
|
||
| Returns: | ||
| Resolved latent heat strategy, or None for isothermal fallback. | ||
| """ | ||
| if latent_heat_strategy is not None: | ||
| return latent_heat_strategy | ||
|
|
||
| latent_heat_array = np.asarray(latent_heat, dtype=np.float64) | ||
| if latent_heat_array.shape == (): | ||
| latent_heat_value = float(latent_heat_array) | ||
| if latent_heat_value > 0: | ||
| return ConstantLatentHeat(latent_heat_value) | ||
| if latent_heat_value < 0: | ||
| logger.warning( | ||
| "Negative latent_heat provided; falling back to " | ||
| "isothermal behavior." | ||
| ) | ||
| return None | ||
|
|
||
| logger.warning( | ||
| "Array-like latent_heat provided; use a LatentHeatStrategy " | ||
| "for per-species values." | ||
| ) | ||
| return None | ||
|
|
||
| def mass_transfer_rate( | ||
| self, | ||
| particle: ParticleRepresentation | ParticleData, | ||
| gas_species: GasSpecies | GasData, | ||
| temperature: float, | ||
| pressure: float, | ||
| dynamic_viscosity: Optional[float] = None, | ||
| ) -> Union[float, NDArray[np.float64]]: | ||
| """Return the mass transfer rate (stub). | ||
|
|
||
| Args: | ||
| particle: Particle representation providing radius and activity | ||
| information. | ||
| gas_species: Gas species supplying vapor properties and | ||
| concentrations. | ||
| temperature: System temperature in Kelvin. | ||
| pressure: System pressure in Pascals. | ||
| dynamic_viscosity: Optional dynamic viscosity override. | ||
|
|
||
| Returns: | ||
| Mass transfer rate per particle and per species in kg/s. | ||
|
|
||
| Raises: | ||
| NotImplementedError: Implemented in E5-F3-P2/P3. | ||
| """ | ||
| raise NotImplementedError("Implemented in E5-F3-P2/P3") | ||
|
|
||
| def rate( | ||
| self, | ||
| particle: ParticleRepresentation | ParticleData, | ||
| gas_species: GasSpecies | GasData, | ||
| temperature: float, | ||
| pressure: float, | ||
| dynamic_viscosity: Optional[float] = None, | ||
| ) -> NDArray[np.float64]: | ||
| """Return the condensation rate (stub). | ||
|
|
||
| Args: | ||
| particle: Particle representation providing radius and activity | ||
| information. | ||
| gas_species: Gas species supplying vapor properties and | ||
| concentrations. | ||
| temperature: System temperature in Kelvin. | ||
| pressure: System pressure in Pascals. | ||
| dynamic_viscosity: Optional dynamic viscosity override. | ||
|
|
||
| Returns: | ||
| Condensation rate per particle and per species in kg/s. | ||
|
|
||
| Raises: | ||
| NotImplementedError: Implemented in E5-F3-P2/P3. | ||
| """ | ||
| raise NotImplementedError("Implemented in E5-F3-P2/P3") | ||
|
|
||
| def step( | ||
| self, | ||
| particle: ParticleRepresentation | ParticleData, | ||
| gas_species: GasSpecies | GasData, | ||
| temperature: float, | ||
| pressure: float, | ||
| time_step: float, | ||
| dynamic_viscosity: Optional[float] = None, | ||
| ) -> ( | ||
| Tuple[ParticleRepresentation, GasSpecies] | Tuple[ParticleData, GasData] | ||
| ): | ||
|
Comment on lines
+1835
to
+1872
|
||
| """Advance one condensation step (stub). | ||
|
|
||
| Args: | ||
| particle: Particle representation to update. | ||
| gas_species: Gas species object providing vapor properties. | ||
| temperature: System temperature in Kelvin. | ||
| pressure: System pressure in Pascals. | ||
| time_step: Integration timestep in seconds. | ||
| dynamic_viscosity: Optional dynamic viscosity override. | ||
|
|
||
| Returns: | ||
| Tuple containing updated particle and gas species objects. | ||
|
|
||
| Raises: | ||
| NotImplementedError: Implemented in E5-F3-P2/P3. | ||
| """ | ||
| raise NotImplementedError("Implemented in E5-F3-P2/P3") | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a keyword argument when constructing
ConstantLatentHeat(e.g.,latent_heat_ref=...) to match how this codebase instantiates it elsewhere and to make the units/meaning of the positional value unambiguous.