Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions .cursor/rules/model-rule-000-organization-of-new-classes.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
---
description: Defines where to place model classes - reusable layers go in physicsnemo/nn, complete models go in physicsnemo/models, with exceptions for example-specific code.
alwaysApply: false
---

When generating or refactoring model code, rule MOD-000 must be followed.
Explicitly reference the rule ID MOD-000 and quote relevant sections when
explaining placement decisions. For example: "Following rule MOD-000, which
states that reusable layers should go in physicsnemo/nn, this
reusable layer should be placed in physicsnemo/nn/..."

## MOD-000: Models organization and where to place new model classes

**Description:**

There are two types of models in PhysicsNeMo:

- Reusable layers that are the building blocks of more complex architectures.
Those should go into `physicsnemo/nn`. Those include for instance
`FullyConnected`, various variants of attention layers, `UNetBlock` (a block
of a U-Net), etc.
All layers that are directly exposed to the user should be imported in
`physicsnemo/nn/__init__.py`, such that they can be used as follows:
```python
from physicsnemo.nn import MyLayer
```
- More complete models, composed of multiple layers and/or other sub-models.
Those should go into `physicsnemo/models`. All models that are directly
exposed to the user should be imported in `physicsnemo/models/__init__.py`,
such that they can be used as follows:
```python
from physicsnemo.models import MyModel
```

The only exception to this rule is for models or layers that are highly specific to a
single example. In this case, it may be acceptable to place them in a module
specific to the example code, such as for example
`examples/<example_name>/utils/nn.py`.

**Rationale:**
Ensures consistency and clarity in the organization of models in the
repository, in particular a clear separation between reusable layers and more
complete models that are applicable to a specific domain or specific data
modality.

**Example:**

```python
# Good: Reusable layer in physicsnemo/nn/attention.py
class MultiHeadAttention(Module):
"""A reusable attention layer that can be used in various architectures."""
pass

# Good: Complete model in physicsnemo/models/transformer.py
class TransformerModel(Module):
"""A complete transformer model composed of attention and feedforward layers."""
def __init__(self):
super().__init__()
self.attention = MultiHeadAttention(...)
self.ffn = FeedForward(...)

# Good: Example-specific utility in examples/weather/utils/nn.py
class WeatherSpecificLayer(Module):
"""Layer highly specific to the weather forecasting example."""
pass
```

**Anti-pattern:**

```python
# WRONG: Complete model placed in physicsnemo/nn/ instead of physicsnemo/models/
# File: physicsnemo/nn/transformer.py
class TransformerModel(Module):
"""Should be in physicsnemo/models/ not physicsnemo/nn/"""
pass

# WRONG: Reusable layer placed in physicsnemo/models/ instead of physicsnemo/nn/
# File: physicsnemo/models/attention.py
class MultiHeadAttention(Module):
"""Should be in physicsnemo/nn/ not physicsnemo/models/"""
pass
```
45 changes: 45 additions & 0 deletions .cursor/rules/model-rule-001-class-inheritance-for-new-models.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
description: All model classes must inherit from physicsnemo.Module (not torch.nn.Module directly) to ensure proper serialization, versioning, and registry functionality.
alwaysApply: false
---

When creating or modifying model classes, rule MOD-001 must be strictly followed. Explicitly reference "Following rule MOD-001" when explaining inheritance decisions and quote the requirement that all models inherit from `physicsnemo.Module`.

## MOD-001: Use proper class inheritance for all models

**Description:**
All model classes must inherit from `physicsnemo.Module`. Direct subclasses of
`torch.nn.Module` are not allowed. Direct subclasses of `physicsnemo.Module`
are allowed (note that `physicsnemo.Module` is a subclass of `torch.nn.Module`).
Ensure proper initialization of parent classes using `super().__init__()`. Pass
the `meta` argument to the `super().__init__()` call if appropriate, otherwise
set it manually with `self.meta = meta`.

**Rationale:**
Ensures invariants and functionality of the `physicsnemo.Module` class for all
models. In particular, instances of `physicsnemo.Module` benefit from features
that are not available in `torch.nn.Module` instances. Those include serialization
for checkpointing and loading modules and submodules, versioning system to
handle backward compatibility, as well as ability to be registered in the
`physicsnemo.registry` for easy instantiation and use in any codebase.

**Example:**

```python
from physicsnemo import Module

class MyModel(Module):
def __init__(self, input_dim: int, output_dim: int):
super().__init__(meta=MyModelMetaData())
self.linear = nn.Linear(input_dim, output_dim)
```

**Anti-pattern:**

```python
from torch import nn

class MyModel(nn.Module):
def __init__(self, input_dim: int, output_dim: int):
self.linear = nn.Linear(input_dim, output_dim)
```
126 changes: 126 additions & 0 deletions .cursor/rules/model-rule-002-models-lifecycle.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
---
description: Defines the 4-stage lifecycle for model classes - experimental, production, pre-deprecation, and deprecation - ensuring structured development and backward compatibility.
alwaysApply: false
---

When creating or moving model classes, rule MOD-002 must be followed.
Explicitly reference "Following rule MOD-002" and state which lifecycle stage
applies. For example: "Following rule MOD-002, which states that new model classes should start in the experimental stage, and that new model classes should go in physicsnemo/experimental/models/, this new model should start in
physicsnemo/experimental/models/..."

## MOD-002: Model classes lifecycle

**Description:**
All model classes must follow the following lifecycle:

- Stage 1: Creation. This is the stage where the model class is created. For
the vast majority of models, new classes are created either in
`physicsnemo/experimental/nn` for reusable layers, or in
`physicsnemo/experimental/models` for more complete models. The `experimental`
folder is used to store models that are still under development (beta or
alpha releases) during this stage, backward compatibility is not guaranteed.
One exception is when the developer is highly confident that the model
is sufficiently mature and applicable to many domains or use cases. In this
case the model class can be created in the `physicsnemo/nn` or `physicsnemo/models`
folders directly, and backward compatibility is guaranteed. Another exception
is when the model class is highly specific to a single example. In this case,
it may be acceptable to place it in a module specific to the example code,
such as for example `examples/<example_name>/utils/nn.py`.

- Stage 2: Production. After staying in stage 1 for a sufficient amount of time
(typically at least 1 release cycle), the model class is promoted to stage 2.
It is then moved to the `physicsnemo/nn` or `physicsnemo/models` folders,
based on the rule `MOD-000`. During this stage, backward compatibility is
guaranteed. Note that a model is not allowed to be promoted to stage 2
without the CI tests required by rule MOD-008.

- Stage 3: Pre-deprecation. For a model class in stage 3 in `physicsnemo/nn` or
`physicsnemo/models`, the developer should start planning its deprecation.
This is done by adding a warning message to the model class, indicating that
the model class is deprecated and will be removed in a future release. The
warning message should be a clear and concise message that explains why the
model class is being deprecated and what the user should do instead. The
deprecation message should be added to both the docstring and should be
raised at runtime. The developer is free to choose the mechanism to raise the
deprecation warning. A model class cannot be deprecated without staying in
stage 3 "pre-deprecation" for at least 1 release cycle.

- Stage 4: Deprecation. After staying in stage 3 "pre-deprecation" for at least 1
release cycle, the model class is deprecated. It can be deleted from the
codebase.

**Rationale:**
This lifecycle ensures a structured approach to model development and maintenance.
The experimental stage allows rapid iteration without backward compatibility
constraints, enabling developers to refine APIs based on user feedback. The
production stage provides stability for users who depend on these models. The
pre-deprecation and deprecation stages ensure users have sufficient time to
migrate to newer alternatives, preventing breaking changes that could disrupt
their workflows. This graduated approach balances innovation with stability,
a critical requirement for a scientific computing framework.

**Example:**

```python
# Good: Stage 1 - New experimental model
# File: physicsnemo/experimental/models/new_diffusion.py
class DiffusionModel(Module):
"""New diffusion model under active development. API may change."""
pass

# Good: Stage 2 - Promoted to production after 1 release cycle
# File: physicsnemo/models/diffusion.py (moved from experimental/)
class DiffusionModel(Module):
"""Stable diffusion model with backward compatibility guarantees."""
pass

# Good: Stage 3 - Pre-deprecation with warning
# File: physicsnemo/models/old_diffusion.py
class DiffusionModel(Module):
"""
Legacy diffusion model.

.. deprecated:: 0.5.0
``OldDiffusionModel`` is deprecated and will be removed in version 0.7.0.
Use :class:`~physicsnemo.models.NewDiffusionModel` instead.
"""
def __init__(self):
import warnings
warnings.warn(
"OldDiffusionModel is deprecated. Use DiffusionModel instead.",
DeprecationWarning,
stacklevel=2
)
super().__init__()

# Good: Stage 4 - Model removed after deprecation period
# (File deleted from codebase)
```

**Anti-pattern:**

```python
# WRONG: New model directly in production folder without experimental phase
# File: physicsnemo/models/brand_new_model.py (should be in experimental/ first)
class BrandNewModel(Module):
"""Skipped experimental stage - risky for stability"""
pass

# WRONG: Breaking changes in production without deprecation cycle
# File: physicsnemo/models/diffusion.py
class DiffusionModel(Module):
def __init__(self, new_required_param): # Breaking change!
# Changed API without deprecation warning - breaks user code
pass

# WRONG: Deprecation without sufficient warning period
# (Model deprecated and removed in same release)

# WRONG: No deprecation warning in code
# File: physicsnemo/models/old_model.py
class OldModel(Module):
"""Will be removed next release.""" # Docstring mentions it but no runtime warning
def __init__(self):
# Missing: warnings.warn(..., DeprecationWarning)
super().__init__()
```
Loading