Skip to content

Commit

Permalink
Merge branch 'joss' of github.com:aymgal/COOLEST into joss
Browse files Browse the repository at this point in the history
  • Loading branch information
gvernard committed Jul 20, 2023
2 parents 4cc3dd3 + 892ca2e commit 9aa589e
Show file tree
Hide file tree
Showing 22 changed files with 1,463 additions and 155 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install flake8 pytest pytest-cov coveralls
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
python -m pip install flake8 pytest-cov coveralls
if [ -f requirements_tests.txt ]; then pip install -r requirements_tests.txt; fi
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
pip install -e .
# - name: Lint with flake8
Expand Down
146 changes: 146 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
<!-- omit in toc -->
# Contributing to COOLEST

Thank you for your interest in COOLEST! All types of feedback, feature proposals and contributions are highly encouraged and valued. See the [Table of Contents](#table-of-contents) for different ways to help and details about how this project handles them. Please make sure to read the relevant section before making your contribution.

> And if you like the project, but just don't have time to contribute, that's fine. There are other easy ways to support the project and show your appreciation, which we would also be very happy about:
> - Star the project
> - Tweet about it
> - Refer this project in your project's readme
> - Mention the project in your talks and tell your friends/colleagues
> - ...and use it for your own research :)
<!-- omit in toc -->
## Table of Contents

- [I Have a Question](#i-have-a-question)
- [I Want To Contribute](#i-want-to-contribute)
- [Reporting Bugs](#reporting-bugs)
- [Suggesting Enhancements](#suggesting-enhancements)
- [Your First Code Contribution](#your-first-code-contribution)
- [Improving The Documentation](#improving-the-documentation)
<!-- - [Styleguides](#styleguides) -->
<!-- - [Commit Messages](#commit-messages) -->
<!-- - [Join The Project Team](#join-the-project-team) -->



## I Have a Question

> If you want to ask a question, we assume that you have read the available [Documentation](coolest.readthedocs.io/).
Before you ask a question, it is best to search for existing [Issues](https://github.com/aymgal/COOLEST/issues) that might help you. In case you have found a suitable issue and still need clarification, you can write your question in this issue. It is also advisable to search the internet for answers first.

If you then still feel the need to ask a question and need clarification, we recommend the following:

- Open an [Issue](https://github.com/aymgal/COOLEST/issues/new).
- Provide as much context as you can about what you're running into.
- Provide project and platform versions (nodejs, npm, etc), depending on what seems relevant.

We will then take care of the issue as soon as possible.

<!--
You might want to create a separate issue tag for questions and include it in this description. People should then tag their issues accordingly.
Depending on how large the project is, you may want to outsource the questioning, e.g. to Stack Overflow or Gitter. You may add additional contact and information possibilities:
- IRC
- Slack
- Gitter
- Stack Overflow tag
- Blog
- FAQ
- Roadmap
- E-Mail List
- Forum
-->

## I Want To Contribute

> ### Legal Notice <!-- omit in toc -->
> When contributing to this project, you must agree that you have authored 100% of the content, that you have the necessary rights to the content and that the content you contribute may be provided under the project license.
### Reporting Bugs

<!-- omit in toc -->
#### Before Submitting a Bug Report

A good bug report shouldn't leave others needing to chase you up for more information. Therefore, we ask you to investigate carefully, collect information and describe the issue in detail in your report. Please complete the following steps in advance to help us fix any potential bug as fast as possible.

- Make sure that you are using the latest version.
- Determine if your bug is really a bug and not an error on your side e.g. using incompatible environment components/versions (Make sure that you have read the [documentation](coolest.readthedocs.io/). If you are looking for support, you might want to check [this section](#i-have-a-question)).
<!-- - To see if other users have experienced (and potentially already solved) the same issue you are having, check if there is not already a bug report existing for your bug or error in the [bug tracker](https://github.com/aymgal/COOLESTissues?q=label%3Abug). -->
- Also make sure to search the internet (including Stack Overflow) to see if users outside of the GitHub community have discussed the issue.
- Collect information about the bug:
- Stack trace (Traceback)
- OS, Platform and Version (Windows, Linux, macOS, x86, ARM)
- Version of the interpreter, compiler, SDK, runtime environment, package manager, depending on what seems relevant.
- Possibly your input and the output
- Can you reliably reproduce the issue?
<!-- - And can you also reproduce it with older versions? -->

<!-- omit in toc -->
#### How Do I Submit a Good Bug Report?

We use GitHub issues to track bugs and errors. If you run into an issue with the project:

- Open an [Issue](https://github.com/aymgal/COOLEST/issues/new).
- Explain the behavior you would expect and the actual behavior.
- Please provide as much context as possible and describe the *reproduction steps* that someone else can follow to recreate the issue on their own. This usually includes your code. For good bug reports you should isolate the problem and create a reduced test case.
- Provide the information you collected in the previous section.

Once it's filed:

- One of the COOLEST developers will try to reproduce the issue with your provided steps. If there are no reproduction steps or no obvious way to reproduce the issue, that developer will ask you for those steps and mark the issue as `needs-repro`. Bugs with the `needs-repro` tag will not be addressed until they are reproduced.
- If we are not able to reproduce the issue, it will be marked `needs-fix`, as well as possibly other tags (such as `critical`), and the issue will be left to be [implemented by someone](#your-first-code-contribution).

<!-- You might want to create an issue template for bugs and errors that can be used as a guide and that defines the structure of the information to be included. If you do so, reference it here in the description. -->


### Suggesting Enhancements

This section guides you through submitting an enhancement suggestion for coolest, **including completely new features and minor improvements to existing functionality**. Following these guidelines will help maintainers and the community to understand your suggestion and find related suggestions.

<!-- omit in toc -->
#### Before Submitting an Enhancement

- Make sure that you are using the latest version of the `coolest` package (most of the time the `main` branch).
- Read the [documentation](coolest.readthedocs.io/) carefully and find out if the functionality is already covered.
- Perform a [search](https://github.com/aymgal/COOLEST/issues) to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one.
- Find out whether your idea fits with the scope and aims of the project. It's up to you to make a strong case to convince the project's developers of the merits of this feature. Keep in mind that we want features that will be useful to the majority of the strong lensing community.

<!-- omit in toc -->
#### How Do I Submit a Good Enhancement Suggestion?

Enhancement suggestions are tracked as [GitHub issues](https://github.com/aymgal/COOLEST/issues).

- Use a **clear and descriptive title** for the issue to identify the suggestion.
- Provide a **step-by-step description of the suggested enhancement** in as many details as possible.
- **Describe the current behavior** and **explain which behavior you expected to see instead** and why. At this point you can also tell which alternatives do not work for you.
- You may want to **include screenshots** which help you demonstrate the steps or point out the part which the suggestion is related to.
- **Explain why this enhancement would be useful** to most COOLEST users. You may also want to point out the other projects that solved it better and which could serve as inspiration.

<!-- You might want to create an issue template for enhancement suggestions that can be used as a guide and that defines the structure of the information to be included. If you do so, reference it here in the description. -->

<!-- ### Your First Code Contribution -->
<!-- TODO
include Setup of env, IDE and typical getting started instructions?
-->

### Improving The Documentation

We also encourage anyone to help improving the documentation, would it be in the code, on the GitHub repository or on the ReadTheDoc website. Please do not hesitate to propose improvements, example notebooks or even add some yourself via pull requests, as described in the previous sections.


<!-- ## Styleguides -->
<!-- ### Commit Messages -->
<!-- TODO
-->

<!-- ## Join The Project Team -->
<!-- TODO -->

<!-- omit in toc -->
## Attribution
This guide is based on the **contributing-gen**. [Make your own](https://github.com/bttger/contributing-gen)!
28 changes: 20 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<img src="docs/_static/coolest_logo.png" width="200" alt="Herculens logo" />
<img src="docs/_static/coolest_logo.png#gh-light-mode-only" width="200" alt="COOLEST logo" />
<img src="docs/_static/coolest_logo_dark_bg.png#gh-dark-mode-only" width="200" alt="COOLEST logo" />

# COOLEST: COde-independent Organized LEns STandard

Expand Down Expand Up @@ -38,21 +39,32 @@ git clone https://github.com/aymgal/COOLEST.git
# Move to the repo directly
cd COOLEST

# (Activate your virtual environment if needed)

# Install
pip install -r requirements.txt
# Now is a good idea to activate your virtual environment if needed
```

# Install the package, optionally in development mode with the -e option
Then you can install the `coolest` package. If you wish to modify or contribute to the source code, do not forget to use the `-e` option in the commands below to install it in development mode. For installing the package without optional dependencies, use the following command:
```bash
# install coolest and required dependencies
pip install [-e] .
```
For installing the package with all optional dependencies, use the following command:
```bash
# also install optional dependencies
pip install [-e] ".[opt]"
```

You can test the installation by running `python -c "import coolest"` in the terminal, or by running some of the example notebooks from the [documentation](docs/notebooks) directory. Moreover, you can run all unit tests (having `pytest` installed in your environment), with the command `pytest test`.

_There will be soon the possibility to install_ `coolest` _directly from PyPi and Conda._

## Contributors

The full list of contributors is given in the [AUTHORS](AUTHORS.md) documents.
## Contributing to the COOLEST standard

We encourage and highly value All types of feedback, feature proposals and contributions from the whole lensing community. Please have a look at [CONTRIBUTING.md](CONTRIBUTING.md) for more information about contributing to the project.


## Original contributors

The full list of contributors is given in [AUTHORS.md](AUTHORS.md).

As this is an open source project, we very much appreciate any feedback and new contributions from the community. The goal is really to improve the standard so that most of the research using strong lens modeling can benefit from it.
8 changes: 1 addition & 7 deletions coolest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,10 @@
# Set the package details
__author__ = 'COOLEST developers'
__email__ = 'aymeric.galan@gmail.com'
__year__ = '2021'
__year__ = '2021-2023'
__credits__ = 'COOLEST developers'
__url__ = 'https://github.com/aymgal/COOLEST'
__description__ = 'Standard for Strong Gravitational Lens Modeling'
__python__ = '>=3.7'
__requires__ = [
'numpy>=1.20.0',
'jsonpickle>=2.0.0',
'astropy>=4.2.0',
] # Package dependencies

# Default package properties
__license__ = 'MIT'
Expand Down
17 changes: 13 additions & 4 deletions coolest/template/classes/lensing_entity_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,18 @@

class LensingEntityList(list, APIBaseObject):
"""The list of components that define the lensing system.
In COOLEST, a 'lensing entity' is typically a galaxy or an external shear.
In COOLEST, a `LensingEntity` is an instance Galaxy or a MassField.
Note that unique identifiers (IDs) for each profile and parameters will be
generated at instantiation time.
- A given profile has a unique IDs with the following pattern:
`{entity index}-{{massfield} or {galaxy}}-{{mass} or {light}}-{profile index}_{profile name}`
- A given parameter has the same ID as above, just with the parameter name at the end:
`{entity index}-{{massfield} or {galaxy}}-{{mass} or {light}}-{profile index}_{profile name}-{parameter name}`
Parameters
----------
Expand All @@ -24,8 +35,6 @@ def __init__(self, *entities: Tuple[LensingEntity]):
self._create_all_ids()

def _create_all_ids(self):
"""Creates a unique identifier for each model profiles and profile parameters.
"""
for i, entity in enumerate(self):
for model_type in ['light', 'mass']:
model = getattr(entity, f'{model_type}_model', None)
Expand All @@ -38,5 +47,5 @@ def _create_all_ids(self):
profile.id = profile_id
if isinstance(profile, AnalyticalProfile):
for param_name, parameter in profile.parameters.items():
param_id = f'{profile.id}-{param_name}'
param_id = util.parameter_to_id(param_name, profile.id)
parameter.id = param_id
11 changes: 7 additions & 4 deletions coolest/template/classes/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@

# Utility functions to use throughout the API

def galaxy_profile_to_id(model_type, profile_name, profile_idx, galaxy_idx):
return f'galaxy_{galaxy_idx}-{model_type}_model-{profile_name}_{profile_idx}'
def galaxy_profile_to_id(model_type, profile_name, profile_idx, entity_idx):
return f'{entity_idx}-galaxy-{model_type}-{profile_idx}-{profile_name}'

def mass_field_profile_to_id(profile_name, profile_idx, field_idx):
return f'massfield_model_{field_idx}-{profile_name}_{profile_idx}'
def mass_field_profile_to_id(profile_name, profile_idx, entity_idx):
return f'{entity_idx}-massfield-mass-{profile_idx}-{profile_name}'

def parameter_to_id(param_name, parent_profile_id):
return f'{parent_profile_id}-{param_name}'

def galaxy_id_to_profile(id_str):
galaxy_str, model_str, profile_str = id_str.split('-')
Expand Down
35 changes: 23 additions & 12 deletions coolest/template/json.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,18 @@ class JSONSerializer(object):
"""Object that write a COOLEST object to a JSON template file,
or loads the content of a JSON template file in the COOLEST format.
For reading, either a plain JSON format or the one generated via jsonpickle can be provided.
For reading, either a plain JSON format or the one generated via `jsonpickle` can be provided.
For the latter, the JSON file should end with the suffix `'_pyAPI'` (before the .json extension).
NOTE: the support for reading the template with `jsonpickle` will probably
be suppressed in the future. Hence we advise users to work with the
pure JSON template files instead.
Parameters
----------
file_path_no_ext : str
Path to the JSON template, or the one to be created.
It should NOT include the .json extension nor the optional _pyAPI
It should NOT include the .json extension nor the optional '_pyAPI' suffix
obj : object, optional
Instance of the COOLEST object (from the `standard` module)
to be encoded, by default None
Expand All @@ -41,6 +45,9 @@ class JSONSerializer(object):
If the provided path contains the .json extension
"""

# suffix to filename to distinguish files that can be read using jsonpickle
_api_suffix = '_pyAPI'

def __init__(self,
file_path_no_ext: str,
obj: object = None,
Expand All @@ -54,8 +61,6 @@ def __init__(self,
self._json_dir = os.path.dirname(file_path_no_ext)
self.obj = obj
self.indent = indent
# to distinguish files that can be converted back to the python API
self._api_suffix = '_pyAPI'
self._check_files = check_external_files

def dump_simple(self, exclude_keys=None):
Expand All @@ -67,10 +72,9 @@ def dump_simple(self, exclude_keys=None):
List of class attributes that should not be included
in the JSON file, by default None
"""
if exclude_keys is None and hasattr(self.obj, 'exclude_keys'):
if exclude_keys is None:
exclude_keys = self.obj.exclude_keys
json_path = self.path + '.json'
result = jsonpickle.encode(self.obj, indent=self.indent)
with open(json_path, 'w') as f:
f.write(self.obj.to_JSON(indent=self.indent, exclude_keys=exclude_keys))

Expand All @@ -86,8 +90,8 @@ def dump_jsonpickle(self):

def load(self, verbose=False):
"""Read the JSON template file and build up the corresponding COOLEST object.
It will first try to load it using `jsonpickle` (if the _pyAPI file exists),
otherwise it will fall back to the manual method.
It will first try to load the '_pyAPI' template if it exists using `jsonpickle`,
otherwise it will fall back to reading the pure json template.
Parameters
----------
Expand All @@ -102,9 +106,9 @@ def load(self, verbose=False):
try:
content = self.load_jsonpickle()
except Exception as e:
if verbose:
print(f"Failed reading with jsonpickle, trying reading pure json"
f" (original error: {e})")
if verbose is True:
print(f"Failed reading '{self._api_suffix}' template with jsonpickle, "
f"now trying with the pure json template (original error: {e})")
content = self.load_simple()
assert isinstance(content, COOLEST)
return content
Expand All @@ -123,6 +127,8 @@ def load_simple(self, as_object=True):
COOLEST object that corresponds to the JSON template
"""
json_path = self.path + '.json'
if not os.path.exists(json_path):
raise ValueError(f"Template file at '{json_path}' does not exist.")
with open(json_path, 'r') as f:
content = json.loads(f.read())
if not as_object:
Expand All @@ -139,6 +145,8 @@ def load_jsonpickle(self):
COOLEST object that corresponds to the JSON template
"""
json_path = self.path + self._api_suffix + '.json'
if not os.path.exists(json_path):
raise ValueError(f"Template file at '{json_path}' does not exist.")
with open(json_path, 'r') as f:
content = jsonpickle.decode(f.read())
return content # COOLEST object
Expand Down Expand Up @@ -244,7 +252,7 @@ def _setup_lensing_entities(self, entities_in):
entities_out = []
for entity_in in entities_in:
entities_out.append(self._setup_lensing_entity(entity_in))
return entities_out
return LensingEntityList(*entities_out)

def _setup_lensing_entity(self, entity_in):
if entity_in['type'] == 'Galaxy':
Expand Down Expand Up @@ -285,6 +293,9 @@ def _update_parameters_values(self, entity_in, entity_out, model_type):

for name, values in profile['parameters'].items():

# pop the id as this was already set at instantiation
_ = values.pop('id', None)

# pixelated profiles, for now only one value given (point estimate)
if 'Grid' in profile['type']:
self._update_grid_parameter(profile_out, name, values)
Expand Down
2 changes: 2 additions & 0 deletions coolest/template/lazy.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@
from coolest.template.classes.mass_field import MassField
from coolest.template.classes.lensing_entity_list import LensingEntityList
from coolest.template.classes.mass_light_model import MassModel, LightModel
from coolest.template.classes.probabilities import Prior, PosteriorStatistics

Loading

0 comments on commit 9aa589e

Please sign in to comment.