cellml / libcellml

Repository for libCellML development.
https://libcellml.org
Apache License 2.0
16 stars 21 forks source link

importSource.model() is None after resolve imports #1165

Closed WeiweiAi closed 1 year ago

WeiweiAi commented 1 year ago

libcellml==0.4.0

I was trying to reuse importSource to import units for different models. I noticed that after resolving the import of the model, I can not access the units model by calling importSource.model(). I think I should set up import every time when I want to import the units instead of reusing it. Anyway, I attached the Python code for the record.

The code will print out:

The import source is valid.
no unresolved imports.
The import source is not valid. 

The last line indicates that importSource.model() is None, which is described above.

from libcellml import Component, Model, Units,  Importer, ImportSource, Parser,Printer,Variable,Validator
from pathlib import PurePath,Path
import os
def resolve_imports(model, base_dir, strict_mode):
    importer = Importer(strict_mode)
    importer.resolveImports(model, base_dir)
    _dump_issues("resolve_imports", importer)
    if model.hasUnresolvedImports():
        print("unresolved imports?")
    else:
        print("no unresolved imports.")
    return importer

def validate_model(model):
    validator = Validator()
    validator.validateModel(model)
    _dump_issues("validate_model", validator)
    return validator.issueCount()

def _dump_issues(source_method_name, logger):
    if logger.issueCount() > 0:
        print('The method "{}" found {} issues:'.format(source_method_name, logger.issueCount()))
        for i in range(0, logger.issueCount()):
            print('    - {}'.format(logger.issue(i).description()))

def parse_model(filename, strict_mode):
    cellml_file = open(filename)
    parser = Parser(strict_mode)
    model = parser.parseModel(cellml_file.read())
    _dump_issues("parse_model", parser)
    cellml_file.close() # added
    return model

def writeCellML(full_path, model):     
    printer = Printer()
    serialised_model = printer.printModel(model)    
    write_file = open(full_path, "w")
    write_file.write(serialised_model)
    write_file.close()

model_path= Path.cwd().as_posix()
units_file = 'units_def.cellml'
import_units_model = parse_model(units_file, True)
model= Model('model_import')
c=Component(model.name())
model.addComponent(c)
var1=Variable('var1')
var1.setUnits('fmol')
units_to_import=['fmol']
c.addVariable(var1)

relative_path_os = os.path.relpath(units_file, model_path)
relative_path=PurePath(relative_path_os).as_posix()
importSource = ImportSource()
importSource.setUrl(relative_path)
importSource.setModel(import_units_model)

if importSource.model() is None:
    print('The import source is not valid.')
else:
    print('The import source is valid.')
    existing_units=set([importSource.model().units(unit_numb).name() for unit_numb in range(importSource.model().unitsCount())])

for unit in units_to_import:
    if unit in existing_units:
        u = Units(unit) 
        u.setImportSource(importSource)
        u.setImportReference(unit)
        model.addUnits(u)

resolve_imports(model, model_path, True) #If this line is commented out, The method "validate_model" found 1 issues:    - Cyclic units exist: 'fmol' -> 'fmol'.

validate_model(model)
model_full_path = model.name()+'.cellml'
writeCellML(model_full_path, model)

model_test= Model('model_test')
c_test=Component(model_test.name())
model_test.addComponent(c_test)

var1=Variable('var1')
var1.setUnits('fmol')
units_to_import=['fmol']
c.addVariable(var1)

if importSource.model() is None:
    print('The import source is not valid.')
else:
    existing_units=set([importSource.model().units(unit_numb).name() for unit_numb in range(importSource.model().unitsCount())])

for unit in units_to_import:
    if unit in existing_units:
        u = Units(unit) 
        u.setImportSource(importSource)
        u.setImportReference(unit)
        model.addUnits(u)
agarny commented 1 year ago

@WeiweiAi, please use the right tags when you insert some code. I have edited your comment and you might want to check it to see what I mean. Without the right tags, you just get some plain code while here we have syntax highlighting which makes it much easier to read.

WeiweiAi commented 1 year ago

Thank you, @agarny

hsorby commented 1 year ago

With the latest development codebase I am getting the same output:

The import source is valid.
no unresolved imports.
The import source is not valid.
hsorby commented 1 year ago

You are not holding onto the importer reference when you call:

resolve_imports(model, model_path, True) #If this line is commented out, The method "validate_model" found 1 issues:    - Cyclic units exist: 'fmol' -> 'fmol'.

If you do instead:

importer = resolve_imports(model, model_path, True) #If this line is commented out, The method "validate_model" found 1 issues:    - Cyclic units exist: 'fmol' -> 'fmol'.

Your code will work. By not holding onto the importer you let go of the imported sources and the Python garbage collection tidies up at some point releasing the imported model.