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
64 changes: 41 additions & 23 deletions heron/asimov/__init__.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,36 @@
import importlib
import pkg_resources
import os
import configparser
import glob
import shutil
import sys

import asimov.pipeline
from asimov import config
import htcondor
from asimov.utils import set_directory
from ..utils import make_metafile

# Handle importlib.resources compatibility across Python versions
if sys.version_info >= (3, 9):
from importlib.resources import files
else:
from importlib_resources import files
Comment on lines +13 to +17

Copilot AI Jan 12, 2026

Copy link

Choose a reason for hiding this comment

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

The code imports importlib_resources for Python versions below 3.9, but this dependency is not listed in requirements.txt or pyproject.toml. Add importlib_resources; python_version < "3.9" to the dependencies to ensure the package is available when needed.

Copilot uses AI. Check for mistakes.

class MetaPipeline(asimov.pipeline.Pipeline):

def build_dag(self, dryrun=False):
def build_dag(self, dryrun=False, psds=None, user=None, clobber_psd=False):
"""
Create a condor submission description.
"""
name = self.production.name # meta['name']
ini = self.production.event.repository.find_prods(name, self.category)[0]

# Construct the command arguments properly
arguments = " ".join(self._pipeline_arguments) + f" {ini}"

description = {
"executable": f"{os.path.join(config.get('pipelines', 'environment'), 'bin', self._pipeline_command)}",
"arguments": f"{self._pipeline_arguments[0]} --settings {ini}",
"arguments": arguments,
"output": f"{name.replace(' ', '_')}.out",
"error": f"{name.replace(' ', '_')}.err",
"log": f"{name.replace(' ', '_')}.log",
Expand All @@ -35,28 +44,37 @@ def build_dag(self, dryrun=False):
with open(f"{name}.sub", "w") as subfile:
subfile.write(job.__str__())

with set_directory(self.production.rundir):
try:
schedulers = htcondor.Collector().locate(
htcondor.DaemonTypes.Schedd, config.get("condor", "scheduler")
)
except configparser.NoOptionError:
schedulers = htcondor.Collector().locate(htcondor.DaemonTypes.Schedd)
schedd = htcondor.Schedd(schedulers)
with schedd.transaction() as txn:
cluster_id = job.queue(txn)

self.production.job_id = int(cluster_id)
self.clusterid = cluster_id
if not dryrun:
with set_directory(self.production.rundir):
try:
schedulers = htcondor.Collector().locate(
htcondor.DaemonTypes.Schedd, config.get("condor", "scheduler")
)
except configparser.NoOptionError:
schedulers = htcondor.Collector().locate(htcondor.DaemonTypes.Schedd)
schedd = htcondor.Schedd(schedulers)
with schedd.transaction() as txn:
cluster_id = job.queue(txn)

self.production.job_id = int(cluster_id)
self.clusterid = cluster_id
self.production.status = "running"
else:
self.logger.info(f"Dry run: would submit job with description: {description}")
self.clusterid = None

def submit_dag(self, dryrun=False):
return self.clusterid
"""Submit the job to the cluster. For MetaPipeline, submission happens in build_dag."""
if hasattr(self, 'clusterid') and self.clusterid is not None:
return self.clusterid
else:
return None

class InjectionPipeline(MetaPipeline):
name = "heron injection"
config_template = importlib.resources.files("heron.asimov") / "heron_template.yml"
config_template = str(files("heron.asimov") / "heron_template.yml")
_pipeline_command = "heron"
_pipeline_arguments = ["injection"]
_pipeline_arguments = ["injection", "--settings"]

def detect_completion(self):

Expand Down Expand Up @@ -112,10 +130,10 @@ class Pipeline(MetaPipeline):
"""

name = "heron"
config_template = importlib.resources.files("heron.asimov") / "heron_template.yml"
config_template = str(files("heron.asimov") / "heron_template.yml")

_pipeline_command = "heron"
_pipeline_arguments = ["inference"]
_pipeline_arguments = ["inference", "--settings"]

def detect_completion(self):

Expand Down Expand Up @@ -213,4 +231,4 @@ def html(self):

out += """</div>"""

return out
return out
91 changes: 52 additions & 39 deletions heron/asimov/heron_template.yml
Original file line number Diff line number Diff line change
@@ -1,56 +1,69 @@
# This is a template for the heron pipeline to interact with asimov
# You should use this in conjuection with the asimov package
# You should use this in conjunction with the asimov package
# rather than directly editing this file to set up an analysis.
# PRODUCTION: {{ production.name }}
# EVENT: {{ production.event.name }}

name: {{ production.name }}

logging: warning
logging:
level: {{ production.meta.get('logging', {}).get('level', 'warning') }}

pages directory: {{ config['general']['webroot'] }}/{{ production.event.name }}/{{ production.name }}

{% if 'injection' in production.meta %}
injection:
parameters: {{ production.meta['injection'] }}
injection model: {{ production.meta['injection']['injection model'] }}
parameters: {{ production.meta['injection']['parameters'] }}
duration: {{ production.meta['injection'].get('duration', 32) }}
sample rate: {{ production.meta['injection'].get('sample rate', 4096) }}
interferometers: {{ production.meta['interferometers'] }}
psds:
{% for ifo in production.meta['interferometers'] -%}
{{ ifo }}: {{ production.meta['injection']['psds'].get(ifo, 'aLIGOZeroDetHighPower') }}
{% endfor %}
{% endif %}

interferometers: {{ production.meta['interferometers'] }}
inference:
name: {{ production.name }}

likelihood:
sample rate: {{ production.meta['likelihood']['sample rate'] }}
interferometers:
{% for ifo in production.meta['interferometers'] -%}
{{ ifo }}: {{ ifo }}
{% endfor %}

{% if "noise model" in production.meta %}
noise model: {{ production.meta['noise model'] }}
{% endif %}
psds:
{% for ifo in production.meta['interferometers'] -%}
{{ ifo }}: {{ production.meta.get('psds', {}).get(ifo, 'aLIGOZeroDetHighPower') }}
{% endfor %}

data: {{ production.meta['data'] }}
{% if 'data' in production.meta %}
data: {{ production.meta['data'] }}
{% endif %}

psds:
{%- for ifo in production.meta['interferometers'] %}
{{ ifo }}: {{ production.psds[ifo] }}
{%- endfor %}
{% if 'event time' in production.meta %}
event time: {{ production.meta['event time'] }}
{% endif %}

{%- assign priors = production.meta['priors'] %}
priors:
{%- if priors.keys() contains "geocentric time" %}{%- assign p = priors['geocentric time'] %}
gpstime: {{p['type'] | default: 'Uniform' }}(name="gpstime", minimum={{p['minimum']}}, maximum={{p['maximum']}}, boundary={{p['boundary'] | default: None}})
{% endif %}
{%- if priors.keys() contains "total mass" %}{% assign p = priors['total mass'] %}{% else %}{% assign p = None %}{% endif %}
total_mass: {{p['type'] | default: "Uniform" }}(name='total_mass', minimum={{p['minimum'] | default: 1}}, maximum={{p['maximum'] | default: 1000}})
{%- if priors.keys() contains "chirp mass" %}{% assign p = priors['chirp mass'] %}{% else %}{% assign p = None %}{% endif %}
chirp_mass: {{p['type'] | default: "Uniform" }}(name='chirp_mass', minimum={{p['minimum'] | default: 1}}, maximum={{p['maximum'] | default: 1000}})
{%- if priors.keys() contains "mass ratio" %}{% assign p = priors['mass ratio'] %}{% else %}{% assign p = None %}{% endif %}
mass_ratio: {{p['type'] | default: "bilby.gw.prior.UniformInComponentsMassRatio" }}(name='mass_ratio', minimum={{p['minimum']}}, maximum={{p['maximum']}})
{%- if priors.keys() contains "luminosity distance" %}{%- assign p = priors['luminosity distance'] %}
luminosity_distance: {{ p['type'] | default: "PowerLaw"}}(name='luminosity_distance', {% for key in p.keys() %}{% if key != "type" %}{{key | replace: " ", "_"}}={{p[key]}},{% endif %} {% endfor %} unit='Mpc')
{% endif %}
{%- if priors.keys() contains "theta jn" %}{% assign p = priors['theta jn'] %}{% else %}{% assign p = None %}
theta_jn: {{p['type'] | default: "Sine" }}(name='theta_jn', minimum={{p['minimum']}}, maximum={{p['maximum']}})
{% endif %}
azimuth: Uniform(name='azimuth', minimum=0, maximum=2 * np.pi, boundary='periodic')
zenith: Sine(name='zenith')
psi: Uniform(name='psi', minimum=0, maximum=np.pi, boundary='periodic')
phase: Uniform(name='phase', minimum=0, maximum=np.pi, boundary='periodic')
{% if 'segment length' in production.meta %}
segment length: {{ production.meta['segment length'] }}
{% endif %}

{% if 'after merger' in production.meta %}
after merger: {{ production.meta['after merger'] }}
{% endif %}

{% if 'likelihood' in production.meta %}
likelihood: {{ production.meta['likelihood'] }}
{% endif %}

{% if 'sampler' in production.meta %}
sampler: {{ production.meta['sampler'] }}
{% endif %}

waveform: {{ production.meta['waveform'] }}
{% if 'priors' in production.meta %}
priors: {{ production.meta['priors'] }}
{% endif %}

report:
location: {{ config['general']['webroot'] }}/{{ production.event.name }}/{{ production.name }}
{% if 'waveform' in production.meta %}
waveform: {{ production.meta['waveform'] }}
{% endif %}
4 changes: 2 additions & 2 deletions heron/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

import numpy as np

# from pesummary.io import read
# from pesummary.core.file.formats.base_read import SingleAnalysisRead
from pesummary.io import read
from pesummary.core.file.formats.base_read import SingleAnalysisRead


def load_yaml(filename):
Expand Down
Loading